您的位置:首页 > 其它

codeforces 121 (比赛) codeforces 191C

2013-08-19 18:57 295 查看
 

哎,CF终于变色了

A题:我写了个二分,囧。。。

B题:模拟题,水

C题:想了半天,水王(一学长)告诉我是DP,果断敲了

dp[i][j]表示i字符走到j字符最大的权值




View Code

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的长度

我的代码




View Code

#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
;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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: