【Codeforces #130 Div2】Solutions
【208A Dubstep】
http://codeforces.ru/problemset/problem/208/A
题目大意:一个句子被添加了若干“WUB”,问原句。
将WUB去掉就行了,在句首或句尾直接删除,在句中替换成空格,注意多个“WUB”相连时特判。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; char s[3000]; int len; bool exist; int main(){ scanf("%s",&s); len=strlen(s); int i=0; while(true){ if(s[i]=='W' && s[i+1]=='U' && s[i+2]=='B') i+=3; else break; } while(true){ if(s[len-1]=='B' && s[len-2]=='U' && s[len-3]=='W') len-=3; else break; } while(i<len){ if(s[i]=='W' && s[i+1]=='U' && s[i+2]=='B'){ i+=3; if(!exist) printf(" "); exist=true; }else{ cout<<s[i]; i++; exist=false; } } cout<<endl; return 0; }
【208B Solitaire】
http://codeforces.ru/problemset/problem/208/B
题目大意:桌面上n堆牌,牌有数值和花色两个属性。一堆可以放到另一堆上当且仅当两堆牌最上面一张的牌面或花色相同。每次可以将最后一堆第x堆放到第x-1堆或x-3堆(如果存在),问是否存在策略使得所有牌合成一堆。
由于n很小,容易想到搜索。但是O(2^n)显然难以令人接受。所以考虑对搜索过程记忆化。f[i][a][b][c]表示:当前处理第i堆,最后一堆是第a堆,倒数第二堆是第b堆,倒数第三堆是第c堆时,是否可以合成一堆,每次搜索后记录结果,避免重复搜索。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; char card[52][2]; int n,f[53][53][53][53]; bool check(char a[2], char b[2]){ return a[0]==b[0] || a[1]==b[1]; } bool dfs(int cur,int a,int b,int c){ bool res=false; if(cur==1) return true; if(f[cur][a][b][c]) return false; if(check(card[a],card[b])) res|=dfs(cur-1,a,c,cur-3); if(!res && cur-3 && check(card[a],card[cur-3])) res|=dfs(cur-1,b,c,a); return !(f[cur][a][b][c]=!res); } int main(){ scanf("%d",&n); memset(f,false,sizeof(f)); for(int i=1;i<=n;i++) scanf("%s",&card[i]); if(dfs(n,n,n-1,n-2)) printf("YES\n"); else printf("NO\n"); return 0; }
【208C Police Station】
http://codeforces.ru/problemset/problem/208/c
题目大意:给定N个节点M条边的无向图,边权相同。在图中选一个点,使得从1到N的最短路上,含有被点覆盖的边的数量的平均值最大。(很别扭,还是看原题吧……)
当时这个题很少有做的,可能是因为不好想。
首先他要求最短路,其次还要求出最短路的数量,然后还要知道每个点分别在多少条最短路径上。
最短路可以用Floyd来求,记为sp;数量可以用DP,f[i][j]表示从1出发走j步到达i的方案数,f[i][j]=Σf[u][j-1],u→i;最后求经过每个点有多少条最短路,可能让人感觉比较麻烦,其实也很简单的。用g[i][j]再统计一下从n出发的方案数,方程跟f[i][j]的一样,那么对于一点x,经过他的最短路条数num就是f[i][j]*g[i][sp-dist[i][j]]。此外还有一个问题就是,如果这个点选在2~N上,每个点将覆盖最短路上的两条边;而如果选在1或N上,只能覆盖最短路上的一条边。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> using namespace std; template<class T>inline void gmax(T &a,T b){if(a<b)a=b;} int n,m,x,y,sp,dist[200],f[200][200],g[200][200],map[200][200],ans; bool vis[200]; double res; struct EDGE{ int pnt; EDGE *pre; EDGE(){} EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){} }Edge[20000],*SP=Edge,*edge[200]; inline void addedge(int a,int b){ edge[a]=new(++SP)EDGE(b,edge[a]); edge[b]=new(++SP)EDGE(a,edge[b]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); addedge(x,y); map[x][y]=map[y][x]=1; } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) if(map[i][k]) for(int j=1;j<=n;j++) if(map[k][j]) if(!map[i][j] || map[i][j]>map[i][k]+map[k][j]) map[i][j]=map[i][k]+map[k][j]; sp=map[1][n]; f[1][0]=1; for(int i=0;i<sp;i++) for(int j=1;j<=n;j++) for(EDGE *k=edge[j];k;k=k->pre) f[k->pnt][i+1]+=f[j][i]; g[n][0]=1; for(int i=0;i<sp;i++) for(int j=1;j<=n;j++) for(EDGE *k=edge[j];k;k=k->pre) g[k->pnt][i+1]+=g[j][i]; for(int i=2;i<n;i++) if(map[1][i]+map[i][n]==sp) gmax(ans,f[i][map[1][i]]*g[i][map[i][n]]); ans<<=1; gmax(ans,f[n][sp]); res=(double)ans/(double)f[n][sp]; printf("%.12lf\n",res); return 0; }
【204D Prizes,Prizes,more Prizes】
http://codeforces.ru/problemset/problem/208/d
题目大意:好像是吃什么东西,每吃一个就会得到多少积分(就像方便面里面兑换卡一样)。有五种奖品,需要积分abcde,主人公一包一包吃,攒到能兑换奖品就去兑换,并且先兑换价值最大的,问最后能兑到五种奖品各多少,还能剩下多少积分。
这个题烂大街了,rating和第一题已经差不多了。
就是把积分累加、累加、累加……低于最小奖品价值的时候就去兑奖,从高往低兑,能兑多少对多少。兑换的时候不能暴力模拟,会TLE。
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; long long n,a[100],b[100],s[100],cur; long long exchange(long long x){ for(int i=5;i>0;i--) b[i]+=x/a[i],x-=(x/a[i]*a[i]); return x; } int main(){ cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; cin>>a[1]>>a[2]>>a[3]>>a[4]>>a[5]; cur=0; for(int i=1;i<=n;i++){ cur+=s[i]; if(cur>=a[1]) cur=exchange(cur); } for(int i=1;i<5;i++) cout<<b[i]<<" "; cout<<b[5]<<endl; cout<<cur<<endl; return 0; }
【208E Blood Cousins】
http://codeforces.ru/problemset/problem/208/e
题目大意:给你若干颗树,若干个询问(x,y),回答在含有结点x的树中有多少结点z,使得x和z关于他们LCA的深度为y。
。。。。。。srO kAc Orz。。。。。。
kAc教的什么树套树、函数式线段树都不会写……只会写一个二分。
首先用倍增法求LCA,然后求LCA下面深度为y的结点个数。
对于统计这一步,可以用dfs序来搞。把时间戳按结点深度压一个vector,每次询问的时候,在与x同深度的时间戳序列里面二分,找到进入LCA子树与离开LCA子树的时间戳之间的节点个数,再减去x自己,就是答案。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <vector> #include <algorithm> #define MN 100010 using namespace std; vector<int> pos[MN],t; int n,m,x,y,deep[MN],l[MN],r[MN],idx,fa[MN][20]; struct EDGE{ int pnt; EDGE *pre; EDGE(){} EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){} }Edge[MN],*SP=Edge,*edge[MN]; inline void addedge(int a,int b){ edge[a]=new(++SP)EDGE(b,edge[a]); } void dfs(int x,int dep){ deep[x]=dep; pos[dep].push_back(idx); l[x]=idx++; for(EDGE *j=edge[x];j;j=j->pre) dfs(j->pnt,dep+1); r[x]=idx; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&fa[i][0]); addedge(fa[i][0],i); } dfs(0,0); for(int j=1;j<17;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; scanf("%d",&m); while(m--){ scanf("%d%d",&x,&y); t=pos[deep[x]]; for(int i=0;i<17;i++) if(y>>i&1) x=fa[x][i]; printf("%d ",x?lower_bound(t.begin(),t.end(),r[x])-lower_bound(t.begin(),t.end(),l[x])-1:0); } return 0; }