2012 Multi-University Training Contest 3

官方题解:http://blog.renren.com/blog/601081183/863771603

1001 Arcane Numbers 1 问任意一个A进制有限小数,能否用B进制有限小数表示。 A进制小数可以转换为X*A^(-n),n为小数部分位数,这个数除以任意次B^(-i),i从1递增,直到得到一个整数,也就是说,如果A的质因子在B中都包含即可。注意A的某个质因子可能大于sqrt(A),这道题就因为这个WA到最后= =


#include 
#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 1000010;
int prime[MAXN];
bool notprime[MAXN];
int primecount = 0;
void getprime()
{
    memset(notprime, 0, sizeof(notprime));
    for (int i = 2; i < MAXN; i++)
    {
        if (!notprime[i])
        {
            prime[primecount++] = i;
            for (int j = 2; i * j < MAXN; j++)
            {
                notprime[i * j] = true;
            }
        }
    }
}
int main()
{
    int t;
    long long a, b;
    cin >> t;
    getprime();
    for (int tt = 1; tt <= t; tt++)
    {
        printf("Case #%d: ", tt);
        cin >> a >> b;
        if (a < 0 || b < 0)
        {
            puts("NO");
            continue;
        }
        if (a == b && a != 0)
        {
            puts("YES");
            continue;
        }
        if (a <= 1 || b <= 1)
        {
            puts("NO");
            continue;
        }
        int end = sqrt(a);
        bool ok = true;
        for (int i = 0; i < primecount; i++)
        {
            if (prime[i] > end) break;
            if (a % prime[i] == 0)
            {
                if (b % prime[i] != 0)
                {
                    ok = false;
                    break;
                }
                while (a % prime[i] == 0)
                {
                    a /= prime[i];
                }
            }
        }
        if (a > 1)
        {
            if (b % a != 0) ok = false;
        }
        if (ok) puts("YES");
        else puts("NO");
    }
    return 0;
}

1004 Magic Number 求字符串间的编辑距离 dp[i][j]可以表示为把第一个字符串前i个字符转换为第二个字符串前j个字符需要的操作数。 dp[i][j] = min(dp[i-1][j]+1(删除原串第i个字符), dp[i][j-1]+1(插入新串第j个字符), dp[i-1][j-1]+(oldstr[i]==newstr[j])?0:1(修改))


#include 
#include 
#include 
#include 
using namespace std;
char magic[1510][15];
char query[15];
inline int min(const int &a;, const int &b;, const int &c;)
{
    if (a < b) return a < c ? a : c;
    else return b < c ? b : c;
}

int main()
{
    int t, n, m;
    int f[15][15];
    int limit;
    scanf("%d", &t;);
    for (int tt = 1; tt <= t; tt++)
    {
        scanf("%d%d", &n;, &m;);
        printf("Case #%d:\n", tt);
        getchar();
        for (int i = 0; i < n; i++) gets(magic[i]);
        for (int ii = 0; ii < m; ii++)
        {
            scanf("%s%d", query, &limit;);
            int ans = 0;
            for (int jj = 0; jj < n; jj++)
            {
                int x = strlen(query);
                int y = strlen(magic[jj]);
                f[0][0] = 0;
                for (int i = 1; i <= x; i++) f[i][0] = i;
                for (int j = 1; j <= y; j++) f[0][j] = j;
                for (int i = 1; i <= x; i++)
                {
                    for (int j = 1; j <= y; j++)
                    {
                        f[i][j] = min(f[i-1][j]+1, f[i][j-1]+1, f[i-1][j-1] + ((query[i-1] == magic[jj][j-1])? 0:1));
                    }
                }
                if (f[x][y] <= limit) ++ans;
            }
            printf("%d\n", ans);
        }
    }
    return 0;
} 

1005 Triangle LOVE 给出一个竞赛图(由无向完全图给每条边加方向构成),问有没有长度为3的环。 方法一,从任意一点出发遍历整个图,搜的时候记录深度,找环。http://euyuil.com/2687/codeforces-117c-cycle/ 官方题解里的增量算法有点晕。。 竞赛图上如果有环,则一定存在长度为3的环。因为若有长度为n的环,一定有长度为n-1的环,画一下就懂了。所以只要DFS判断有没有环即可。 最简单的想法:若存在两个点出度相同则存在长度为3的环。先找出两个点A和B,若A到B有边,此时A出度为1,B出度为0。对于其他任意一点C,有以下四种情况:AB出度都加一,AB出度不变、A出度加一,B出度不变、A出度不变,B出度加一。要想使AB出度相同,必须有B的出度加一、C的出度不变的情况,B到C、C到A有边才可以。这样ABC就形成了一个环。 神奇的图论啊!

1006 Flowers 花在一段时间内开放,问某一时刻最多开多少花。 很明显,离散化区间更新的线段树,没想到这道题过的人这么多。。大家都好强啊。 把每个区间离散为三个点,如[1,4]分为[1,1],[2,3]和[4,4]即可。


#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 100010;
struct flower
{
    int s, t;
}f[MAXN];
struct Node
{
    int l, r;
    int sum, inc;
}tree[MAXN * 4 * 4];
int mycount;
int a[MAXN * 2];

void build(int num, int l, int r)
{
    tree[num].l = l;
    tree[num].r = r;
    int mid = (l + r) / 2;
    if (l < r)
    {
        build(num * 2, l, mid);
        build(num * 2 + 1, mid + 1, r);
    }
}

int convert(int x)
{
    int left = 0;
    int right = mycount - 1;
    int mid;
    while (left < right)
    {
        mid = (left + right) / 2;
        if (x > a[mid]) left = mid + 1;
        else right = mid;
    }
    return left * 2;
}

void getsegment(int x, int &s;, int &t;)
{
    int left = 0;
    int right = mycount - 1;
    int mid;
    while (left <= right)
    {
        mid = (left + right) / 2;
        if (x == a[mid])
        {
            s = mid * 2;
            t = mid * 2;
            return;
        }
        if (x > a[mid] && x < a[mid + 1])
        {
            s = mid * 2 + 1;
            t = mid * 2 + 1;
            return;
        }
        if (x > a[mid])
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }
}

void add(int num, int l, int r, int value)
{
    if (tree[num].l == l && tree[num].r == r)
    {
        tree[num].inc += value;
        return;
    }
    int mid = (tree[num].l + tree[num].r) / 2;
    if (l > mid) add(num * 2 + 1, l, r, value);
    else if (r <= mid) add(num * 2, l, r, value);
    else
    {
        add(num * 2, l , mid, value);
        add(num * 2 + 1, mid + 1, r, value);
    }
}

int query(int num, int l, int r)
{
    if (l == tree[num].l && r == tree[num].r)
    {
        return tree[num].sum + tree[num].inc;
    }
    int mid = (tree[num].l + tree[num].r) / 2;
    if (tree[num].inc)
    {
        add(num * 2, tree[num].l, mid, tree[num].inc);
        add(num * 2 + 1, mid + 1, tree[num].r, tree[num].inc);
        tree[num].sum += tree[num].inc;
        tree[num].inc = 0;
    }
    if (l > mid) return query(num * 2 + 1, l, r);
    else if (r <= mid) return query(num * 2, l , r);
    else
    {
        return query(num * 2, l, mid) + query(num * 2 + 1, mid + 1, r);
    }
}

int main()
{
    int t;
    int n, m;
    cin >> t;
    for (int tt = 1; tt <= t; tt++)
    {
        memset(tree, 0, sizeof(tree));
        printf("Case #%d:\n", tt);
        cin >> n >> m;
        int temp = 0;
        for (int i = 0; i < n; i++)
        {
            scanf("%d%d", &f;[i].s, &f;[i].t);
            a[temp++] = f[i].s;
            a[temp++] = f[i].t;
        }
        sort(a, a + temp);
        mycount = unique(a, a + temp) - a;
        build(1, 0, (mycount - 1) * 2);
        for (int i = 0; i < n; i++)
        {
            add(1, convert(f[i].s), convert(f[i].t), 1);
        }
        int pos;
        for (int i = 0; i < m; i++)
        {
            scanf("%d", &pos;);
            if (pos < a[0] || pos > a[mycount - 1])
            {
                printf("0\n");
                continue;
            }
            int s, t;
            getsegment(pos, s, t);
            printf("%d\n", query(1, s, t));
        }
    }
    return 0;
} 

1009 Cut the cake 对于颜色相间的正方形,用dp[i][j]表示右下角为(i,j)的最大矩形边长,实时更新答案,首先判断(i,j),(i-1,j),(i,j-1)这三个点,若颜色不合法则记为1。若dp[i][j-1]等于dp[i-1][j],要判断左上角那个点的颜色是否符合。若dp[i][j-1]不等于dp[i-1][j],dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + 1。 对于颜色相同的矩形,其实就是一个找最大子矩形的问题,推荐王知昆的论文《浅谈用极大化思想解决最大子矩形问题》,用到里面第二种方法。 定义悬线为从矩形上边,上端点覆盖了一个障碍点或达到整个矩形上端的有效竖线(不覆盖任何障碍点),把悬线尽量左右移动,就可以形成一个矩形。考虑每个悬线(最多NM个)最多能移动多少就能求出最大子矩形。需要left[i][j],right[i][j],height[i][j]三个数组,分别记录,往左、往右、往上能移动到哪里。从上往下扫,然后每行从左往右、从右往左各扫一遍,leftmost记录最左非障碍点位置,left[i][j] = max(left[i-1][j], leftmost),右边同理。遇到障碍点就把left[i][j]置为1,right[i][j]置为m,height[i][j]置为0。O(NM)


#include 
#include 
#include 
#include 
using namespace std;
const int MAXN = 1010;
char map[MAXN][MAXN];
int dp[MAXN][MAXN];
int height[MAXN][MAXN], l[MAXN][MAXN], r[MAXN][MAXN];
int work(char ch, const int &n;, const int &m;)
{
    memset(height, 0, sizeof(height));
    int ans = 0;
    int leftmost , rightmost;
    for (int j = 1; j <= m; j++)
    {
        l[0][j] = 1;
        r[0][j] = m;
    }
    for (int i = 1; i <= n; i++)
    {
        leftmost = 1;
        rightmost = m;
        for (int j = 1; j <= m; j++)
        {
            if (map[i][j] != ch)
            {
                leftmost = j + 1;
                height[i][j] = 0;
                l[i][j] = 1; //不影响下一行的min 和 max
                r[i][j] = m;
            }
            else
            {
                height[i][j] = height[i-1][j] + 1;
                l[i][j] = max(l[i-1][j], leftmost);
            }
        }
        for (int j = m; j >= 1; j--)
        {
            if (map[i][j] == ch)
            {
                r[i][j] = min(r[i-1][j], rightmost);
                ans = max(ans, (r[i][j] - l[i][j] + 1 + height[i][j]) * 2);
            }
            else
            {
                rightmost = j - 1;
            }
        }
    }
    return ans;
}
int main()
{
    char s[MAXN];
    int t, n, m;
    cin >> t;
    for (int tt = 1; tt <= t; tt++)
    {
        scanf("%d%d", &n;, &m;);
        gets(s);
        for (int i = 1; i <= n; i++) gets(map[i] + 1);
        int ans = 0;
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (i > 1 && j > 1)
                {
                    if (map[i][j] == map[i][j-1] || map[i][j] == map[i-1][j]) dp[i][j] = 1;
                    else
                    {
                        if (dp[i][j-1] != dp[i-1][j]) dp[i][j] = min(dp[i][j-1], dp[i-1][j]) + 1;
                        else
                        {
                            int count = dp[i][j-1];
                            if (map[i-count][j-count] == map[i][j]) dp[i][j]  = count + 1;
                            else dp[i][j] = count;
                        }
                    }
                }
                else dp[i][j] = 1;
                if (dp[i][j] > ans) ans = dp[i][j];
            }
        }
        ans *= 4;
        ans = max(ans, work('B', n, m));
        ans = max(ans, work('R', n, m));
        printf("Case #%d: %d\n", tt, ans);
    }
    return 0;
}

1010 MAP 我擦啊,这么简单的题意,说得这么恶心。用 map 和 set 写起来挺爽的,貌似数据都是按顺序的,用不到 map… C++ 还有 istringstream 这么神奇的东西啊。貌似这题数据是\r\n,所以。。如果gets读换行符就会被坑了= =


#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int main()
{
    int t, n;
    string url;
    map<string, int> querymap;
    set urlset[105];
    int c[105];
    cin >> t;
    for (int tt = t; tt <= t; tt++)
    {
        cin >> n;
        string name;
        string line;
        querymap.clear();
        memset(c, 0, sizeof(c));
        double ans = 0;
        for (int i = 0; i < n; i++)
        {
            cin >> name;
            querymap.insert(pair<string, int>(name, i));
            urlset[i].clear();
            getline(cin, line);
            istringstream iss(line);
            while (iss >> url)
            {
                //cout << url << endl;
                c[i]++;
                urlset[i].insert(url);
            }
        }
        for (int i = 0; i < n; i++)
        {
            cin >> name;
            int index = querymap[name];
            getline(cin, line);
            istringstream iss(line);
            int r = 0;
            int count = 0;
            double sum = 0;
            while (iss >> url)
            {
                ++r;
                if (urlset[index].count(url))
                {
                    ++count;
                    sum += count / (double)r;
                }
            }
            if (c[i]) ans += sum / c[i];
        }
        printf("Case #%d: %.6lf\n", tt, ans / n);
    }
    return 0;
} 


comments powered by Disqus