代码改变世界

7月25-集训个人赛第四场

2013-07-25 10:06  bootstar  阅读(187)  评论(0编辑  收藏  举报

 

A Codeforces 182D Common Divisors

求两个字符串的公共子串的个数。

首先我们分别对两个串计算其KMPnext函数值,然后利用next函数值,可以得到每个串是由哪个子串(保证这个子串的长度是所有满足的子串中最小的一个)重复多少次得到的。如果这里不明白,建议先去做一下POJ 2406。然后就是判断这两个子串是不是一致的,如果一致,直接计算两个重复次数的最大公约数,然后对最大公约数进行分解计算因子个数就可以得到结果了。

 

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define maxn 100005
 4 char src1[maxn], src2[maxn];
 5 int f1[maxn], f2[maxn];
 6 
 7 int get(char *src, int f[]){
 8     int i, c;
 9     for(i = 2, c = 0; src[i]; i ++){
10         while(src[c + 1] != src[i] && c){
11             c = f[c];
12         }
13         if(src[c + 1] == src[i]){
14             f[i] = c + 1;
15             c ++;
16         }
17         else{
18             f[i] = 0;
19             c = 0;
20         }
21     }
22     return i - 1;
23 }
24 bool cmp(char *s, char *d, int n){
25     while(n && (*s ++) == (*d ++)){
26         n --;
27     }
28     if(n) return false;
29     return true;
30 }
31 int gcd(int x, int y){
32     return y == 0 ? x : gcd(y, x%y);
33 }
34 int factor(int n){
35     int r = 1;
36     for(int i = 2; i*i <= n; i ++){
37         int t = 1;
38         while(n%i==0){
39             n/=i;
40             t ++;
41         }
42         r = r * t;
43     }
44     if(n!=1) r *= 2;
45     return r;
46 }
47 int main(){
48     scanf("%s", src1 + 1);
49     scanf("%s", src2 + 1);
50     int len1 = get(src1, f1);
51     int len2 = get(src2, f2);
52     int la = len1, lb = len2;
53     if(len1%(len1 - f1[len1])==0){
54         la = len1 - f1[len1];
55     }
56     if(len2%(len2 - f2[len2])==0){
57         lb = len2 - f2[len2];
58     }
59     if(la == lb && cmp(src1 + 1, src2 + 1, la)){
60         int l = gcd(len1/la, len2/lb);
61         printf("%d\n", factor(l));
62     }
63     else{
64         printf("0\n");
65     }
66     return 0;
67 }
View Code

 

B Codeforces 276E Little Girl and Problem on Trees

线段树的题目。对于树上的操作转化为区间操作,然后用线段树维护。浙大三月月赛的时候A题是这个类型的题目,如果懂得这个模型,不难想到先对树进行dfs,得到一个dfs序列。注意到除了根节点之外,其他点的度数最多是2,除了1之外每个点顶多只有一个儿子节点。对于将距离为d的节点都增加某个值,那么有向下走和向上走两个方向。那么只要得到向上与向下的更新区间即可。不过由于1节点有多个孩子节点,如果d是大于当前点到1号节点的距离的时候,也需要去更新其他子树,一个办法是直接暴力更新,这种不用多想肯定会超时,另一个办法是在维护一个线段树,这棵线段树记录距离根节点为d的区间增加的值。

查询的答案就变成两部分,一个是维护dfs序的线段树中的值,另一个是距离根节点的d的区间增加的值。更多此类题目:

http://blog.sina.com.cn/s/blog_45e6be08010171d8.html

 

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <vector>
  4 #include <algorithm>
  5 using namespace std;
  6 #define maxn 100005
  7 
  8 struct Tree{
  9     #define lson(x) (x<<1)
 10     #define rson(x) (x<<1|1)
 11     #define mid(x, y) ((x + y)>>1)
 12 
 13     int f[maxn*4];
 14     Tree(){
 15         memset(f, 0, sizeof(f));
 16     }
 17     void push_Down(int c){
 18         f[lson(c)] += f[c];
 19         f[rson(c)] += f[c];
 20         f[c] = 0;
 21     }
 22     void update(int c, int l, int r, int left, int right, int d){
 23         if(l <= left && right <= r){
 24             f[c] += d;
 25             return;
 26         }
 27         push_Down(c);
 28         int m = mid(left, right);
 29         if(r <= m)
 30             update(lson(c), l, r, left, m, d);
 31         else if(l > m)
 32             update(rson(c), l, r, m + 1, right, d);
 33         else{
 34             update(lson(c), l, m, left, m, d);
 35             update(rson(c), m + 1, r, m + 1, right, d);
 36         }
 37     }
 38     int query(int c, int p, int left, int right){
 39         if(p == left && p == right){
 40             return f[c];
 41         }
 42         push_Down(c);
 43         int m = mid(left, right);
 44         if(p <= m) return  query(lson(c), p, left, m);
 45         return query(rson(c), p, m + 1, right);
 46     }
 47 };
 48 Tree root_tree, tree;
 49 vector<int> G[maxn];
 50 int depth[maxn], L[maxn], R[maxn], loc[maxn], col[maxn], cnt;
 51 
 52 void dfs(int v, int f, int c, int d){
 53     depth[v] = d;
 54     col[v] = c;
 55     loc[v] = ++ cnt;
 56     for(int i = 0; i < G[v].size(); i ++){
 57         if(G[v][i] == f) continue;
 58         dfs(G[v][i], v, c, d + 1);
 59     }
 60 }
 61 
 62 int main(){
 63     int n, q, x, y, root_cnt = 0;
 64     scanf("%d%d", &n, &q);
 65     for(int i = 1; i < n; i ++){
 66         scanf("%d%d", &x, &y);
 67         G[x].push_back(y);
 68         G[y].push_back(x);
 69     }
 70     cnt = 0;
 71     for(int i = 0; i < G[1].size(); i ++){
 72         L[i] = cnt + 1;
 73         dfs(G[1][i], 1, i, 1);
 74         R[i] = cnt;
 75     }
 76     for(int i = 0, cmd, d; i < q; i ++){
 77         scanf("%d%d", &cmd, &x);
 78         if(cmd == 0){
 79             scanf("%d%d", &y, &d);
 80             if(x != 1){
 81                 int l = max(L[col[x]], loc[x] - d);
 82                 int r = min(R[col[x]], loc[x] + d);
 83                 tree.update(1, l, r, 1, n, y);
 84                 if(depth[x] <= d) root_cnt += y;
 85                 if(depth[x] < d){
 86                     root_tree.update(1, 1, d - depth[x], 1, n, y);
 87                     r = min(L[col[x]] + d - depth[x] - 1, R[col[x]]);
 88                     tree.update(1, L[col[x]], r, 1, n, -y);
 89                 }
 90             }
 91             else{
 92                 root_cnt += y;
 93                 root_tree.update(1, 1, d, 1, n, y);
 94             }
 95         }
 96         else{
 97             if(x == 1) printf("%d\n", root_cnt);
 98             else printf("%d\n", tree.query(1, loc[x], 1, n) +
 99                         root_tree.query(1, depth[x], 1, n));
100         }
101     }
102     return 0;
103 }
View Code

 

C详见这里:http://www.cnblogs.com/bootstar/p/3209643.html

 

D详见这里:http://www.cnblogs.com/bootstar/p/3206313.html

 

E一个简单的组合数学题。

模型是这样的:00010001000..这样的,那么在两端的0显然只有一种方式放。

11中间的0可以从左侧放也可以从右侧放,假设有k个连续的0,那么就有2^(k-1)种放法。然后就是一个排列组合。

 

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 1005;
 7 const int mod = 1000000007;
 8 typedef long long LL;
 9 LL C[maxn][maxn];
10 int a[maxn];
11 void init(){
12     C[0][0] = 1;
13     for(int i = 1; i <= 1000; i ++){
14         C[i][0] = 1;
15         for(int j = 1; j <= i; j ++){
16             C[i][j] = (C[i-1][j] + C[i-1][j-1])%mod;
17         }
18     }
19 }
20 LL quick_power(LL a, int b){
21     LL ret = 1;
22     if(b<=0) return 1;
23     for(;b;b>>=1){
24         if(b&1) ret = ret * a%mod;
25         a = a * a % mod;
26     }
27     return ret;
28 }
29 int main(){
30     //freopen("test.in", "r", stdin);
31     init();
32     for(int n, m; scanf("%d%d", &n, &m)!=EOF;){
33         for(int i = 0; i < m; i ++){
34             scanf("%d", &a[i]);
35         }
36         sort(a, a + m);
37         LL ans = 1;
38         int k, s = 1, f = 0, t = n - m;
39         for(int i = 0; i < m; i ++){
40             k = a[i] - s;
41             if(f){
42                 ans = ((ans * C[t][k])%mod) * quick_power(2, k-1)%mod;
43             }
44             else ans = ans * C[t][k]%mod;
45             s = a[i] + 1;
46             f = 1;
47             t -=k;
48         }
49         printf("%I64d\n", ans);
50     }
51     return 0;
52 }
View Code

 

F类似于上次个人赛里面B题。维护一个数组,用于记录这个数p的最近被添加进来的位置。对于x被添加进来,那么对x进行因子分解,然后去看每个因子上一次更新的位置与当前位置的差值满不满足条件。同时对数组进行更新。还是直接看代码吧。。。

 

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define maxn 100005
 4 int used[maxn];
 5 
 6 int process(int x, int y, int index){
 7     int ans = 0;
 8     for(int i = 1; i*i <= x ; i ++){
 9         if(x%i==0){
10             if(used[i] < index - y) ans ++;
11             used[i] = index;
12             if(used[x/i] < index - y) ans ++;
13             used[x/i] = index;
14         }
15     }
16     return ans;
17 }
18 
19 int main(){
20     for(int n, x, y; scanf("%d", &n)!=EOF; ){
21         memset(used, 0, sizeof(used));
22         for(int i = 1; i <= n; i ++){
23             scanf("%d%d", &x, &y);
24             printf("%d\n", process(x, y, i));
25         }
26     }
27 }
View Code