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 }
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 }
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 }
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 }
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 }
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 }