Codeforces Round #338

Codeforces Round #338
比赛链接:http://codeforces.com/contest/615
通过数:1
Final standing:1360
比赛状态不是很好,因为期考周有个半个星期没做题,然而当时是第二天有操系考试的情况下做的,基本能做就很开心了。
A:
问n个数中是不是有数字没有出现过

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 100 + 5;
int vis[MAXN];
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(vis, 0, sizeof(vis));
        for(int i = 0 ; i < n ; i++){
            int u;
            scanf("%d", &u);
            int v;
            while(u--){
                scanf("%d", &v);
                vis[v] = 1;
            }
        }
        int ok = 1;
        for(int i = 1 ; i <= m ; i++){
            if(vis[i] == 0){
                ok = 0;
                break;
            }
        }
        if(ok)  printf("YES\n");
        else    printf("NO\n");
    }
    return 0;
}

B:
刚开始没看懂题,然后各种看错。
赛中加了个特判过了pretest然后WA了。
主要是题意,注意刺猬的尾巴可以只有一个点,然后所有的针都是扎在尾巴端点(最后面那个点上的就可以了)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define LL long long
const int MAXN = 200000 + 5;
vector<int>lin[MAXN];
LL d[MAXN], dp[MAXN];
int vis[MAXN];
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        for(int i = 1 ; i <= n ; i++)   lin[i].clear(), d[i] = 0, dp[i] = 1, vis[i] = 0;
        for(int i = 1 ; i <= m ; i++){
            int u, v;
            scanf("%d%d", &u, &v);
            if(u > v) swap(u, v);
            d[u]++, d[v]++;
            vis[v] = 1;
            lin[u].push_back(v);
        }
        LL ans = 2;
        for(int i = 1 ; i <= n ; i++){
// if(i == 5){
// printf("dp[i] = %I64d, d[i] =%I64d\n", dp[i], d[i]);
// }
// if(vis[i])
                ans = max(ans, d[i] * dp[i]);
            for(int j = 0 ; j <(int)lin[i].size() ; j++){
                int v = lin[i][j];
                dp[v] = max(dp[v], dp[i] + 1);
// printf("i = %d, v = %d\n", i, v);
            }
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

C:
赛后看题解,完全没有思路,只知道是dp,但是dp的长度怎么取就不知道了。
实际上,对于每个t中的点,设它用s中字符串匹配t中点最远能到len那么长,那么t - t+len都可以匹配了。
设lcp[i][j]表示s和t中以i和j开头的最长公共前缀,写到这里后面自己慢慢就可以磨出来。
其实这种模型以前见过。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 2100 + 5;
char s[MAXN], t[MAXN];
int lcp1[MAXN][MAXN], lcp2[MAXN][MAXN], lcp[MAXN];
int dp[MAXN];
int path[MAXN];
int dir[MAXN], re[MAXN];
int x[MAXN], y[MAXN];
int cnt;
int main()
{
    while(scanf("%s%s", s, t) != EOF){
        int len1 = strlen(s);
        int len2 = strlen(t);
        memset(lcp1, 0, sizeof(lcp1));
        memset(lcp2, 0, sizeof(lcp2));
        for(int i = len1 ; i > 0 ; i--) for(int j = len2 ; j > 0 ; j--) if(s[i - 1] == t[j - 1])  lcp1[i][j] = lcp1[i+1][j+1] + 1;
        for(int i = 1 ; i <= len1 ; i++) for(int j = len2 ; j > 0 ; j--) if(s[i - 1] == t[j - 1])  lcp2[i][j] = lcp2[i-1][j+1] + 1;
// for(int i = 1 ; i <= len1 ; i++){
// for(int j = 1 ; j <= len2 ; j++) printf("%d ", lcp1[i][j]);
// printf("\n");
// }
// printf("\n");
// for(int i = 1 ; i <= len1 ; i++){
// for(int j = 1 ; j <= len2 ; j++) printf("%d ", lcp2[i][j]);
// printf("\n");
// }
// printf("\n");
        memset(lcp, 0, sizeof(lcp));
        for(int i = 1 ; i <= len1 ; i++){
            for(int j = 1 ; j <= len2 ; j++){
                if(lcp[j] < lcp1[i][j]) dir[j] = 1, lcp[j] = lcp1[i][j], re[j] = i;
                if(lcp[j] < lcp2[i][j]) dir[j] = -1, lcp[j] = lcp2[i][j], re[j] = i;
            }
        }
        for(int i = 1 ; i <= len2 + 1 ; i++)    path[i] = i;
        memset(dp, -1, sizeof(dp));
        dp[1] = 0;
        for(int i = 1 ; i <= len2 ; i++){
            if(dp[i] == -1) continue;
            for(int j = 1 ; j <= lcp[i] ; j++){
                if(dp[i + j] == -1 || dp[i + j] > dp[i] + 1)    dp[i + j] = dp[i] + 1, path[i + j] = i;
            }
        }
// for(int i = 1 ; i <= len2 ; i++) printf("%d ", dp[i]);
// printf("\n");
        printf("%d\n", dp[len2 + 1]);
        cnt = 0;
        if(dp[len2 + 1] != -1){
            int now = len2 + 1;
            while(path[now] != now){
                x[cnt] = re[path[now]], y[cnt++] = re[path[now]] + (now - path[now] - 1) * dir[path[now]];
                now = path[now];
            }
            for(int i = cnt - 1 ; i >= 0 ; i--) printf("%d %d\n", x[i], y[i]);
        }
    }
    return 0;
}

D:
赛中想了然而没有过样例,只完成了剥壳的工作。简单来说划归到最后就是每个素因子用几次的问题。设每中素因子有cnt[i]个,则总的选法为(cnt[2] + 1) * (cnt[3] + 1) …这样。然后WA了几发以后发现这个指数过于庞大,必须要取模。直接模1e9+7会错,因为一点道理没有。脑子一动想到费马小定理转一下发现a^(1e9+6) = 1,然后自然就化简了。
但是1e9+6不是一个素数,也就是说没有逆元。所以每个素因子的指数上标就没法求了啊……
有两种方法。标解和代码一样,还有一种计算l[i]计算从左到i是指数乘积,r[i]计算从右到左时指数乘积。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define LL long long
#define mod (1000000007)
const int MAXN = 200000 + 500;
LL cnt[MAXN];
LL mul(LL a, LL b)
{
    LL ans = 1;
    while(b){
        if(b & 1)   ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans;
}
LL ppow(LL a, LL x)
{
    LL ans = 1;
    while(x){
        if(x & 1)   ans = (ans * a) % mod;
        a = (a * a) % mod;
        x >>= 1;
    }
    return ans;
}
LL rev(LL a){return ppow(a, mod - 2);}
int main()
{
    int n;
    while(scanf("%d", &n) != EOF){
        memset(cnt, 0, sizeof(cnt));
        int u;
        for(int i = 0 ; i < n ; i++)    scanf("%d", &u), cnt[u]++;
        LL sum = 1;
        LL ans = 1;
        for(int i = 1 ; i < MAXN ; i++){
            if(cnt[i]){
                LL a = ppow(i, (cnt[i] + 1) * cnt[i] / 2);
                ans = ppow(ans, cnt[i] + 1) * ppow(a, sum) % mod;
                sum = (sum * (cnt[i] + 1)) % (mod - 1);
// printf("i = %d, cnt[i] = %I64d, a = %I64d, sum = %I64d\n", i, cnt[i], a, sum);
            }
        }
        ans = (ans % mod + mod) % mod;
        printf("%I64d\n", ans);
    }
    return 0;
}

E:
把每次连接下一个六边形的边旋转一下,就发现变成了一个个的标准六边形,然后使劲调整下标就可以。
Dis表示六边形边长,需要O(1)求不然会TLE。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define LL long long
LL cal(LL u){return u * (u - 1) * 3;}
int main()
{
    LL n;
    while(scanf("%I64d", &n) != EOF){
        if(n == 0){
            printf("0 0\n");
            continue;
        }
        LL tn = n;
        LL dis = sqrt(1.0 * n / 3) + 3;
        while(cal(dis - 1) >= n)    dis--;
//        printf("dis = %I64d\n", dis);
        n -= cal(dis - 1);
        int flag = 0;
        int f = 1;
        while(n){
            LL dx = 0;
//            if(f) dx = dis - 2, f = 0;
//            else
                dx = dis - 1;
//            printf("n = %I64d, dx = %I64d, flag = %d\n", n, dx, flag);
            if(n > dx) n -= dx, flag++;
            else    break;
        }
//        if(tn == 60){
//            printf("flag = %d, n = %I64d, dis = %I64d\n", flag, n, dis);
//        }
        dis -= 1;
        LL x, y, tx, ty;
        if(flag == 0)   x = 2 * dis , y = 0, tx = -1, ty = 2;
        else if(flag == 1)   x = dis, y = 2 * dis, tx = -2, ty = 0;
        else if(flag == 2)   x = -dis, y = 2 * dis, tx = -1, ty = -2;
        else if(flag == 3)   x = -2 * dis, y = 0, tx = 1, ty = -2;
        else if(flag == 4)   x = -dis, y = -2 * dis, tx = 2, ty = 0;
        else  x = dis, y = -2 * dis, tx = 1, ty = 2;
//        printf("flag = %d, x = %I64d, y = %I64d, tx = %I64d, ty = %I64d, dis = %I64d, n = %I64d\n", flag, x, y, tx, ty, dis, n);
        x += tx * n;
        y += ty * n;
//        if(flag == 0)   x -= tx, y -= ty;
        printf("%I64d %I64d\n", x, y);
    }
    return 0;
}

你可能感兴趣的:(Codeforces Round #338)