代码改变世界

8月5日集训队个人赛第七场解题报告

2013-08-05 11:39  bootstar  阅读(216)  评论(0编辑  收藏  举报

A Codeforces 204A

简单的数位DP。从高位到低位扫一遍就可以了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 typedef long long LL;
 4 const int maxn = 20;
 5 LL p[maxn];
 6 
 7 LL DP(LL x){
 8     int bit[20], cnt = 0;
 9     for(LL n = x; n; bit[cnt ++] = n%10, n/=10);
10     if(cnt==1) return x - 1;
11     LL ans = 9;
12     for(int i = 2; i <= cnt - 1; i ++)
13         ans = ans + 9 * p[i-2];
14     for(int i = cnt - 1, j = 1; i > 0; i --, j ++){
15         if(i == cnt - 1) ans = ans + (bit[i] - 1) * p[cnt - j - 1];
16         else ans = ans + bit[i] * p[cnt - j - 1];
17     }
18     if(bit[0] > bit[cnt-1]) ans ++;
19     return ans;
20 }
21 
22 int main(){
23     p[0] = 1;
24     for(int i = 1; i <= 18; i ++) p[i] = p[i-1] * 10;
25     for(LL l, r; scanf("%I64d%I64d", &l, &r)!=EOF; printf("%I64d\n", DP(r+1) - DP(l)));
26     return 0;
27 }
View Code

B Codeforces 120F

树形DP,寻找一棵树的最长直径。dp[i][2],0表示以i为根的子树中最长的链,1表示以i为根的子树中次长链,其中最长链与次长链没有共同的边只有一个i点位公共的根。第一次dfs求dp数组,第二次dfs时求以每个点为根的时候的最长直径,答案在i的子树中,最长链与次长链的和或者最长链与从父亲边过来的最长的链的和。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 100+5;
 7 int f[maxn], e[maxn*2], t[maxn*2];
 8 int d[maxn][2], c[maxn][2];
 9 int tot;
10 
11 void add(int u, int v){
12     e[tot] = f[u];
13     t[tot] = v;
14     f[u] = tot ++;
15 }
16 void dfs1(int u, int fa){
17     d[u][0] = d[u][1] = 0;
18     c[u][0] = c[u][1] = 0;
19     for(int i = f[u]; i != -1; i = e[i]){
20         int v = t[i];
21         if(v==fa) continue;
22         dfs1(v, u);
23         if(d[v][0] + 1 <= d[u][1]) continue;
24         d[u][1] = d[v][0] + 1;
25         c[u][1] = v;
26         if(d[u][1] > d[u][0]){
27             swap(d[u][1], d[u][0]);
28             swap(c[u][1], c[u][0]);
29         }
30     }
31 }
32 void dfs2(int u, int fa, int l, int &ans){
33     ans = max(ans, d[u][0] + d[u][1]);
34     ans = max(ans, d[u][0] + l);
35     for(int i = f[u]; i != -1; i=e[i]){
36         int v = t[i];
37         if(v==fa) continue;
38         if(v != c[u][0])
39             dfs2(v, u, max(l + 1, d[u][0] + 1), ans);
40         else
41             dfs2(v, u, max(l + 1, d[u][1] + 1), ans);
42     }
43 }
44 int main(){
45     freopen("input.txt", "r", stdin);
46     freopen("output.txt", "w", stdout);
47     for(int n, ans; scanf("%d", &n)!=EOF;){
48         ans = 0;
49         for(int i = 1, m; i <= n; i ++){
50             scanf("%d", &m);
51             memset(f, -1, sizeof(f));tot=0;
52             for(int i = 1, u, v; i < m; i ++){
53                 scanf("%d%d", &u, &v);
54                 add(u, v);
55                 add(v, u);
56             }
57             dfs1(1, 0);
58             int tmp = 0; dfs2(1, 0, 0, tmp);
59             ans = ans + tmp;
60         }
61         printf("%d\n", ans);
62     }
63     return 0;
64 }
View Code

C Codeforces 29D

LCA+模拟。由于点比较少,直接用并查集,不适用路径压缩,并且对每个点维护一个到根的距离。然后每次就直接查最近公共祖先,然后模拟走就可以了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 #define maxn 310
 6 
 7 int dfn[maxn], f[maxn], head[maxn], order[maxn];
 8 int euler[maxn*2], e[maxn*2], g[maxn*2];
 9 int tot, pos;
10 int stack[maxn];
11 
12 void add(int u, int v){
13     e[tot] = head[u];
14     g[tot] = v;
15     head[u] = tot ++;
16 }
17 
18 int dfs(int v, int fa, int depth){
19     dfn[v] = depth + 1;
20     f[v] = fa;
21     int son = 0, flag = 1;
22     for(int i = head[v] ; i != -1; i = e[i]){
23         int u = g[i];
24         if(u==fa) continue;
25         son += dfs(u, v, depth+1);
26         flag = 0;
27     }
28     son += flag;
29     return son;
30 }
31 
32 int getAncestor(int u, int v){
33     for(;dfn[u]!=dfn[v];){
34         if(dfn[u]>dfn[v]) u = f[u];
35         else v = f[v];
36     }
37     for(;u!=v; u = f[u], v = f[v]);
38     return u;
39 }
40 
41 void up(int v, int fa){
42     for(v = f[v];v!=fa;v = f[v]){
43         euler[pos ++] = v;
44     }
45     euler[pos ++] = fa;
46 }
47 void down(int v, int fa){
48     int top = 0;
49     for(v; v != fa; v = f[v]){
50         stack[top++] = v;
51     }
52     for(;top; euler[pos ++] = stack[--top]);
53 }
54 
55 int main(){
56     //freopen("test.in", "r", stdin);
57     for(int n; scanf("%d", &n)!=EOF;){
58         tot = pos = 0;
59         memset(head, -1, sizeof(head));
60         for(int i = 1, x, y; i < n; i ++){
61             scanf("%d%d", &x, &y);
62             add(x, y);
63             add(y, x);
64         }
65         int leafn = dfs(1, 1, 1);
66         for(int i = 0; i < leafn; i ++){
67             scanf("%d", &order[i]);
68         }
69         euler[pos ++] = 1;
70         down(order[0], 1);
71         for(int i = 1, last = order[0]; i < leafn; i ++){
72             int ancestor = getAncestor(last, order[i]);
73             //printf(">> ancestor: %d\n", ancestor);
74             up(last, ancestor);
75             down(order[i], ancestor);
76             last = order[i ];
77             if(pos > 2*n-1){
78                 break;
79             }
80         }
81         up(order[leafn-1], 1);
82         if(pos > 2*n-1){
83             printf("-1\n");
84         }
85         else{
86             for(int i = 0; i < pos; i ++){
87                 printf("%d ", euler[i]);
88             }
89             printf("\n");
90         }
91     }
92     return 0;
93 }
View Code

D Codeforces 128B

优先队列模拟就可以了。先把长度为1的丢进去,然后每次取出一个元素后,进行扩展(+1位)

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <queue>
 4 #include <algorithm>
 5 #include <string>
 6 #include <iostream>
 7 #include <functional>
 8 using namespace std;
 9 
10 struct Node{
11     string first;
12     int second;
13     bool operator<(const Node &a)const{
14         return first > a.first;
15     }
16 };
17 Node make_node(string s, int p){
18     Node a;
19     a.first = s, a.second = p;
20     return a;
21 }
22 
23 int main(){
24     string a, s = "";
25     int k, i, pos;
26     cin>>a; cin>>k;
27     priority_queue<Node> Q;
28     for(i = 0; i < a.size(); i ++){
29         Q.push(make_node(s + a[i], i));
30     }
31     for(i = 0; i < k - 1 && !Q.empty(); i ++){
32         s = Q.top().first;
33         pos = Q.top().second;
34         Q.pop();
35         if(pos + 1 < a.size()){
36             Q.push(make_node(s + a[pos + 1], pos + 1));
37         }
38     }
39     if(i < k - 1 || i == k - 1 && Q.empty()){
40         cout<<"No such line."<<endl;
41     }
42     else cout<<Q.top().first<<endl;
43     return 0;
44 }
View Code

E Codeforces 178B1~178B3

求从一个点到另一个点的路径上最少经过的割边是多少。

先用tarjan求边双连通分量,然后染色,将割边记录下来,然后建树。然后对树做欧拉环游,并得到每个点到根的距离,对得到的序列做RMQ,对于询问x, y

其结果就是dfn[col[x]] + dfn[col[y]] - dfn[ance],其中ance时col[x], col[y]的最近公共祖先。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn = 100005;
 6 const int maxm = 100005;
 7 typedef int arr[maxn];
 8 arr head, dfn, low, col, st;
 9 int top, depth, color, tot, cnt;
10 pair<int, int> e[maxm*2], bridge[maxn];
11 int euler[maxn*3], d[maxn*3][30], pos;
12 
13 void add(int x, int y){
14     e[tot] = make_pair(head[x], y);
15     head[x] = tot ++;
16 }
17 void tarjan(int u, int f){
18     dfn[u] = low[u] = ++depth;
19     st[++top] = u;
20     for(int i = head[u]; i != -1; i = e[i].first){
21         int v = e[i].second;
22         if(v==f) continue;
23         if(!dfn[v]){
24             tarjan(v, u);
25             low[u] = min(low[v], low[u]);
26         }
27         else low[u] = min(low[u], dfn[v]);
28         if(low[v] > dfn[u])
29             bridge[cnt ++] = make_pair(u, v);
30     }
31     if(dfn[u] == low[u]){
32         int x; color ++;
33         do{
34             col[x = st[top--]] = color;
35         }while(x != u);
36     }
37 }
38 void dfs(int u, int f){
39     euler[++ pos] = u; low[u] = pos;
40     for(int i = head[u]; i != -1; i = e[i].first){
41         int v = e[i].second;
42         if(v==f) continue;
43         dfn[v] = dfn[u] + 1;
44         dfs(v, u);
45         euler[++pos] = u;
46     }
47 }
48 void RMQ_init(){
49     for(int i = 1; i <= pos; i ++) d[i][0] = i;
50     for(int j = 1; (1<<j) <= pos; j ++){
51         for(int i = 1; i + j - 1<= pos; i ++){
52             if(dfn[euler[d[i][j-1]]] < dfn[euler[d[i + (1<<(j-1))][j-1]]]){
53                 d[i][j] = d[i][j-1];
54             }
55             else d[i][j] = d[i + (1<<(j-1))][j-1];
56         }
57     }
58 }
59 int RMQ(int L, int R){
60     int k = 0;
61     if(L > R) swap(L, R);
62     while((1<<(k+1)) <= R-L+1) k ++;
63     return dfn[euler[d[L][k]]] < dfn[euler[d[R-(1<<k)+1][k]]] ? d[L][k] : d[R-(1<<k)+1][k];
64 }
65 int main(){
66     //freopen("test.in", "r", stdin);
67     for(int n, m, Q; scanf("%d%d", &n, &m)!=EOF; ){
68         depth = color = tot = top = cnt =0;
69         for(int i = 1; i <= n; i ++){
70             head[i] = -1, dfn[i] = 0;
71         }
72         for(int i = 1, x, y; i <= m; i ++){
73             scanf("%d%d", &x, &y);
74             add(x, y);
75             add(y, x);
76         }
77         tarjan(1, -1);
78         tot = 0;
79         for(int i = 0; i <= color; i ++){
80             head[i] = -1;
81             dfn[i] = 0;
82         }
83         for(int i = 0; i < cnt; i ++){
84             add(col[bridge[i].first], col[bridge[i].second]);
85             add(col[bridge[i].second], col[bridge[i].first]);
86         }
87         pos = dfn[1] = 0;
88         dfs(1, 0);
89         RMQ_init();
90         scanf("%d", &Q);
91         for(int i = 1, x, y; i <= Q; i ++){
92             scanf("%d%d", &x, &y);
93             int ance = RMQ(low[col[x]], low[col[y]]); ance = euler[ance];
94             printf("%d\n", dfn[col[x]] + dfn[col[y]] - 2 * dfn[ance]);
95         }
96     }
97     return 0;
98 }
View Code

F URAL 1671

并查集。倒着搞就是了,先将没有删除的边合并,然后按照从后往前加边就是了。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int maxn = 100005;
 7 const int maxm = 100000+10;
 8 int f[maxn], h[maxm], s[maxm];
 9 bool mark[maxm];
10 pair<int, int> edge[maxm];
11 
12 int find(int x){
13     return x ==f[x] ? x : (f[x] = find(f[x]));
14 }
15 
16 int main(){
17     for(int n, m;scanf("%d%d", &n, &m)!=EOF;){
18         for(int i = 1; i <= n; i ++) f[i] = i;
19         for(int i = 1, x, y; i <= m; i ++){
20             scanf("%d%d", &x, &y);
21             edge[i] = make_pair(x, y);
22         }
23         int q, cnt = n; scanf("%d", &q);
24         for(int i = 1, x; i <= q; i ++){
25             scanf("%d", &x);
26             mark[x] = 1;
27             h[i] = x;
28         }
29         for(int i = 1; i <= m; i ++){
30             if(!mark[i]){
31                 int x = find(edge[i].first);
32                 int y = find(edge[i].second);
33                 if(x == y) continue;
34                 f[x] = y;
35                 cnt --;
36             }
37         }
38         for(int i = q; i >= 1; i --){
39             s[i] = cnt;
40             int x = find(edge[h[i]].first);
41             int y = find(edge[h[i]].second);
42             if(x==y) continue;
43             f[x] = y;
44             cnt --;
45         }
46         for(int i = 1; i <= q; i ++){
47             if(i != 1) printf(" ");
48             printf("%d", s[i]);
49         }
50         printf("\n");
51     }
52     return 0;
53 }
View Code