zoukankan      html  css  js  c++  java
  • bzoj 3697: 采药人的路径【点分治】

    点分治,设当前处理的块的重心为rt,预处理出每个子树中f[v][0/1]表示组合出、没组合出一对值v的链数(从当前儿子出发的链),能组合出一对v值就是可以有一个休息点
    然后对于rt,经过rt且合法的路径是两边拼起来至少有一个休息点的路径,每次假如新儿子都和之前的儿子组合一遍即可,注意f[0][0]实际上也是有休息点的,因为组合出0就是休息点,另外加一下

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=200005;
    int n,h[N],cnt,rt,mxde,sm,t[N],mx[N],si[N],de[N],dis[N];
    long long f[N][2],g[N][2],ans;
    bool v[N];
    struct qwe
    {
    	int ne,to,va;
    }e[N<<1];
    int read()
    {
    	int r=0,f=1;
    	char p=getchar();
    	while(p>'9'||p<'0')
    	{
    		if(p=='-')
    			f=-1;
    		p=getchar();
    	}
    	while(p>='0'&&p<='9')
    	{
    		r=r*10+p-48;
    		p=getchar();
    	}
    	return r*f;
    }
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].ne=h[u];
    	e[cnt].to=v;
    	e[cnt].va=w;
    	h[u]=cnt;
    }
    void gtrt(int u,int fa)
    {
    	si[u]=1;
    	mx[u]=0;
    	for(int i=h[u];i;i=e[i].ne)
    		if(e[i].to!=fa&&!v[e[i].to])
    		{
    			gtrt(e[i].to,u);
    			si[u]+=si[e[i].to];
    			mx[u]=max(mx[u],si[e[i].to]);
    		}
    	mx[u]=max(mx[u],sm-si[u]);
    	if(mx[u]<mx[rt])
    		rt=u;
    }
    void dfs(int u,int fa)
    {
    	de[u]=de[fa]+1;
    	mxde=max(mxde,de[u]);
    	if(t[dis[u]])
    		f[dis[u]][1]++;
    	else
    		f[dis[u]][0]++;
    	t[dis[u]]++;
    	for(int i=h[u];i;i=e[i].ne)
    		if(e[i].to!=fa&&!v[e[i].to])
    		{
    			dis[e[i].to]=dis[u]+e[i].va;
    			dfs(e[i].to,u);
    		}
    	t[dis[u]]--;
    }
    void wk(int u)
    {
    	int mx=0;
    	v[u]=1,g[n][0]=1;
    	for(int i=h[u];i;i=e[i].ne)
    		if(!v[e[i].to])
    		{
    			dis[e[i].to]=n+e[i].va;
    			de[e[i].to]=1;
    			mxde=1;
    			dfs(e[i].to,0);
    			mx=max(mx,mxde);
    			ans+=(g[n][0]-1)*f[n][0];
    			for(int j=-mxde;j<=mxde;j++)
    				ans+=g[n-j][1]*f[n+j][1]+g[n-j][0]*f[n+j][1]+g[n-j][1]*f[n+j][0];
    			for(int j=n-mxde;j<=n+mxde;j++)
    				g[j][0]+=f[j][0],g[j][1]+=f[j][1],f[j][0]=f[j][1]=0;
    		}
    	for(int i=n-mx;i<=n+mx;i++)
    		g[i][0]=g[i][1]=0;
    	for(int i=h[u];i;i=e[i].ne)
    		if(!v[e[i].to])
    		{
    			sm=si[e[i].to];
    			rt=0;
    			gtrt(e[i].to,0);
    			wk(rt);
    		}
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<n;i++)
    	{
    		int x=read(),y=read(),z=((read()==1)?1:-1);
    		add(x,y,z),add(y,x,z);
    	}
    	sm=mx[0]=n;
    	gtrt(1,0);
    	wk(rt);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    C#遍历访问Dictionary和HashTable
    SQL:select case when(转)
    微软四大名著
    中国 Erlounge III 归来,好消息一箩筐!
    原来,程序的世界远比我想象的精彩
    Google和Yahoo专家联手揭秘世界顶尖公司的技术内幕
    有意义,不容易!(一位译者的翻译感言)
    世界级Oracle专家权威力作
    国内第一部DWR著作
    SQL Server故障排除圣经
  • 原文地址:https://www.cnblogs.com/lokiii/p/10103063.html
Copyright © 2011-2022 走看看