codeforces 121 (比赛) codeforces 191C
哎,CF终于变色了
A题:我写了个二分,囧。。。
B题:模拟题,水
C题:想了半天,水王(一学长)告诉我是DP,果断敲了
dp[i][j]表示i字符走到j字符最大的权值
int dp[30][30]; int max(int a,int b){ return a>b?a:b; } int main() { int n,i,j,k; char s[15]; while(scanf("%d",&n)!=EOF) { memset(dp,0,sizeof(dp)); for(k=1;k<=n;k++) { scanf("%s",s); int len=strlen(s); for(i=0;i<26;i++) { if(dp[i][s[0]-'a']) dp[i][s[len-1]-'a']=max(dp[i][s[len-1]-'a'],dp[i][s[0]-'a']+len); } if(len>dp[s[0]-'a'][s[len-1]-'a']) dp[s[0]-'a'][s[len-1]-'a']=len; } int ans=0; for(i=0;i<26;i++) { if(dp[i][i]>ans) ans=dp[i][i]; } printf("%d\n",ans); } return 0; }
D:
从右往左维护一个堆或者一个multiset就好了
E:
题意:给你一颗树,再给你一些点对,数据范围都是10W,对于每一个点对,这两个点之间的简单路径上的点都会被经过一次
最后输出每条边被经过的次数
这题最神奇了,赛后搞了很长时间,死磕着大婶们的各种代码,终于在一个夜黑风高的晚上被我搞出来了,特此mark一下我的死磕精神,hehe
先搜一遍树,得出时间戳以及各个点的深度等的信息,目的是为了找点对的LCA(最近公共祖先),以及把树变成有向的
下面就是核心的地方了
记录一个数组c[];
对于一对点 (u v) 他们的LCA(u,v)=X
c[u]++;c[v]++;c[x]+=2;也就是说当某个点是点对中的一个点是,就在标记数组中加个1,如果是某两个点的最近公共祖先,就加个2
开始一直不理解这样子要干嘛,无奈之下我只能搞了组数据,单步调试,调了许久,总算是搞清楚这个标记数组的神奇之处了
好了不废话了,看图
如图所示的一棵树(已经调整了方向,以1为根),现在假如有两对点(12 , 14) (14 , 15)则
c[12]=1,c[14]=2,c[15]=1,la[7]=2,la[3]=2;
然后我们开始第二次dfs,判断每条边被经过了几次 主要是在回溯过程中完成的。
每次在回溯时都会把子树节点相应的信息传递上来,比如从v回溯到u,我们就要先判断有几个v的子节点会 “冲出” v 经过u->v之间的边
当然这个数量肯定不是子树中被标记的数量,因为可能某两个点的LCA也在子树内,这两个点就不会上来了,即不会经过u-v之间的边了
所以一个LCA可以阻止子树内的两个点(即输入的某对点对)走出这棵子树(这就解释了la[x]+=2这一步),在程序中就表示为: 回溯到某个点时,先减去这个点的la[u]的值,表示又少了la[u]数量的点走上来,然后从v回溯到u的过程就可以得出u->v之间的边被经过的次数了,回溯完了,所有的答就案都出来了
以上图为例:图中红色的左边上的数字是这几条边的编号(为了方便图中只用到这几条边)
先遍历到12,开始往6回溯,edge[1]被经过了一次,edge[1]=1;13 和 6 之间的边没有被 经过,所以回溯到6的时候
总共有sum=1的点走上来了,继续往3回溯,先减去6的la[]值(为0),所以,6与3之间的边被经过了几次也可以得出来了
edge[4]=sum+c[6] c[6]为0,所以edge[4]=1;
右边的回溯情况:
14、15回溯到7,edge[2]加了2,edge[3]加了1,sum=3再从7回溯到3的过程中发现7是lca,所以先减去la[7]的值
此时 sum=1,相当于7的子树内有一个点会走出来,经过edge[5],这样子edge[5]=1;继续这样回溯回去就好了。。。。。。
这样复杂度就为求LCA的复杂度了,2*n*log(2*n) 记录的时间戳序列为2*n的长度
我的代码
#include<string.h> #include<stdio.h> #include<vector> #include<math.h> using namespace std; const int M =100100; const double inf = 1e20; int min(int a,int b){return a<b?a:b;} int n,k,tdfn,tot; int dp[20][2*M],vis[M]; int B[2*M],LOG[2*M],used[M],F[2*M],pos[M],c[M],la[M]; vector<int> edge[M],eg[M]; int ans[M]; void rmq_init(int n,int num[]) { int i,j; for(j=1;j<=n;j++) dp[0][j]=num[j]; for(j=1;j<=LOG[n];j++) { int limit=n+1-(1<<j); for(i=1;i<=limit;i++) { int x=i+(1<<j>>1); dp[j][i]=min(dp[j-1][x],dp[j-1][i]); } } } int rmq(int l,int r,int num[]) { int m=LOG[r-l+1]; return min(dp[m][l],dp[m][r-(1<<m)+1]); } void dfs(int s) { int i,t; used[s]=1; int tmp=++tdfn; B[++tot]=tmp;F[tmp]=s; pos[s]=tot; int sz=edge[s].size(); for(i=0;i<sz;i++) { t=edge[s][i]; if(used[t]) continue; dfs(t); B[++tot]=tmp;//backtrack } used[s]=0; } int lca(int a,int b) { if(pos[a]>pos[b]) swap(a,b); int ans=rmq(pos[a],pos[b],B); return F[ans]; } void init() { tdfn=0; tot=0; memset(used,0,sizeof(used)); dfs(1); rmq_init(tot,B); } int solve(int u,int edge_num)//核心 { int sum=0; used[u]=1; int sz=edge[u].size(); for(int i=0;i<sz;i++) { int to=edge[u][i]; if(used[to]) continue; sum+=solve(to,eg[u][i]); } sum-=la[u]; if(edge_num) ans[edge_num]=sum+c[u]; return ans[edge_num]; } int main() { int i,n,m,a,b,w; LOG[0]=-1; for(i=1;i<2*M;i++) LOG[i]=LOG[i>>1]+1; while(scanf("%d",&n)!=EOF) { for(i=0;i<=n;i++){ edge[i].clear();c[i]=0;} char str[5]; for(i=1;i<n;i++) { scanf("%d%d",&a,&b); edge[a].push_back(b); edge[b].push_back(a); eg[a].push_back(i); eg[b].push_back(i); } init(); scanf("%d",&k); while(k--) { scanf("%d%d",&a,&b); c[a]++; c[b]++; int x=lca(a,b); la[x]+=2; } solve(1,0); for(i=1;i<n;i++) printf("%d ",ans[i]); puts(""); } return 0; }