您的位置:首页 > 其它

Codecraft-18 and Codeforces Round #458 (Div. 1 + Div. 2, combined) E. Palindromes in a Tree(点分治)

2018-02-26 20:58 363 查看
题目链接:http://codeforces.com/contest/914/problem/E

点分治,a到t只有20个字母,状压表示每个字母出现的奇偶性,然后点分治,计数的时候正着扫一边然后反着扫一边即可,然后就是打标记的小技巧,优化常数。

代码:#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace Point_divide
{
const int MAXN=2e5+10;
const int M=(1<<20)+5;
int N,num,Max,flag,root;
struct node
{
int v,next;
}edge[MAXN*2];
int head[MAXN],tot;
int size[MAXN];//树的大小
int maxv[MAXN];//最大孩子节点的size
bool vis[MAXN];
int dis[MAXN],tmpv[MAXN];
char s[MAXN];
int val[MAXN];
ll ans[MAXN],mp[M];
int book[M];
void init(int _n)
{
N=_n;tot=0;flag=0;
memset(ans,0,sizeof(ans));
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
}
void addedge(int u,int v)
{
edge[tot].v=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void upd(int now,int add)
{
if(book[now]!=flag)
{
mp[now]=0;
book[now]=flag;
}
mp[now]+=add;
}
ll qry(int now)
{
if(book[now]!=flag)
return 0;
return mp[now];
}
//处理子树的大小
void dfssize(int u,int f)
{
size[u]=1;
maxv[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==f||vis[v])continue;
dfssize(v,u);
size[u]+=size[v];
if(size[v]>maxv[u])maxv[u]=size[v];
}
}
//找重心
void dfsroot(int r,int u,int f)
{
if(size[r]-size[u]>maxv[u])//size[r]-size[u]是u上面部分的树的尺寸,跟u的最大孩子比,找到最大孩子的最小差值节点
maxv[u]=size[r]-size[u];
if(maxv[u]<Max)Max=maxv[u],root=u;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v==f||vis[v])continue;
dfsroot(r,v,u);
}
}
//求每个点离重心的距离
ll dfsans(int u,int d,int f)
{
ll ret=0;
dis[num++]=d;
ret+=qry(d^val[root]);
for(int i=0;i<=19;i++)
{
int fi=(d^val[root])^(1<<i);
ret+=qry(fi);
}
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v!=f&&!vis[v])
ret+=dfsans(v,d^val[v],u);
}
ans[u]+=ret;
return ret;
}
ll dfsrootans(int u,int d,int f)
{
ll ret=0;
if(__builtin_popcount(d^val[root])<=1)
{
ret++;
}
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(v!=f&&!vis[v])
ret+=dfsrootans(v,d^val[v],u);
}
ans[u]+=ret;
return ret;
}
//计算以u为根的子树中有多少点对的距离小于等于K
void calc(int u)
4000

{
ll tmpans=0;
int totv=0;
for(int i=head[root];~i;i=edge[i].next)
{
int v=edge[i].v;
if(vis[v])
continue;
tmpv[totv++]=v;
}
flag++;
for(int i=0;i<totv;i++)
{
int v=tmpv[i];
num=0;
tmpans+=dfsans(v,val[v],root);
for(int j=0;j<num;j++)
{
upd(dis[j],1);
}
}
flag++;
reverse(tmpv,tmpv+totv);
for(int i=0;i<totv;i++)
{
int v=tmpv[i];
num=0;
dfsans(v,val[v],root);
for(int j=0;j<num;j++)
{
upd(dis[j],1);
}
}
ans[root]+=tmpans;
for(int i=0;i<totv;i++)
{
int v=tmpv[i];
ans[root]+=dfsrootans(v,val[v],root);
}
}
void dfs(int u)
{
Max=N;
dfssize(u,0);
dfsroot(u,u,0);
vis[root]=1;
calc(root);
for(int i=head[root];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
if(!vis[v])
{
dfs(v);
}
}
}
}
using namespace Point_divide;
int n;
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
scanf("%d",&n);
init(n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
scanf("%s",s+1);
for(int i=1;i<=n;i++)
{
val[i]=1<<(s[i]-'a');
}
dfs(1);
for(int i=1;i<=n;i++)
{
printf("%lld ",ans[i]+1LL);
}
printf("\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐