• 模板—点分治A(容斥)(洛谷P2634 [国家集训队]聪聪可可)


    洛谷P2634 [国家集训队]聪聪可可

    静态点分治

    一开始还以为要把分治树建出来……
    • 树的结构不发生改变,点权边权都不变,那么我们利用刚刚的思路,有两种具体的分治方法。
    • A:朴素做法,直接找重心,处理过重心的所有路径。然而,路径端点在同一子树(即路径实际上并不过重心)的情况会发生重复计数,需要使用类似容斥的方法,不断删去重复计数的部分。
    • B:采用类似树形背包的思路,遍历子树时,只考虑当前子树和先前处理完的多颗子树之间的路径,以保证路径端点在不同的子树中,防止重复计数,不需要麻烦的容斥。在一些更为复杂的情况下,方法A不能解决问题,可以在方法B的基础上结合数据结构来解决。因此,方法B适用范围更广,细节更少不易错(个人观点),推荐大家使用。

    感觉桶比较难以理解……容斥就是先更新整颗树的答案,然后处理lca是子树的答案。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define MAXN 20010
    #define LL long long
    #define INF 1000000000
    using namespace std;
    struct edge
    {
    	int u,v,w,nxt;
    	#define u(x) ed[x].u
    	#define v(x) ed[x].v
    	#define w(x) ed[x].w
    	#define n(x) ed[x].nxt
    }ed[MAXN*2];
    int first[MAXN],num_e;
    #define f(x) first[x]
    int n;
    int sum,mn,root;
    int size[MAXN],mxsize[MAXN];
    bool v[MAXN];
    LL ans,cnt[3];
    void getroot(int x,int fa)//找根节点
    {
    	size[x]=1,mxsize[x]=0;
    	for(int i=f(x);i;i=n(i))
    	if(v(i)!=fa&&!v[v(i)])
    	{
    		getroot(v(i),x);
    		size[x]+=size[v(i)];
    		mxsize[x]=max(mxsize[x],size[v(i)]);
    	}
    	mxsize[x]=max(mxsize[x],sum-size[x]);
    	if(mxsize[x]<mn)mn=mxsize[x],root=x;
    }
    void ask(int x,int fa,int l)
    {
    	cnt[l%3]++;
    	for(int i=f(x);i;i=n(i))
    	if(v(i)!=fa&&!v[v(i)])
    		ask(v(i),x,(l+w(i))%3);
    }
    int solve(int x,int add)
    {
    	cnt[0]=cnt[1]=cnt[2]=0;
    	ask(x,0,add);
    	return 2ll*cnt[1]*cnt[2]+cnt[0]*cnt[0];
    }
    void divide(int x)
    {
    	ans+=solve(x,0),v[x]=1;//更新答案
    	for(int i=f(x);i;i=n(i))
    	if(!v[v(i)])
    	{
    		ans-=solve(v(i),w(i));//容斥去重
    		sum=size[v(i)],mn=INF;	
    		getroot(v(i),0);
    		divide(root);
    	}
    }
    int gcd(int a,int b){return !b?a:gcd(b,a%b);}
    inline void add(int u,int v,int w);
    inline int read()
    {
    	int s=0;char a=getchar();
    	while(a<'0'||a>'9')a=getchar();
    	while(a>='0'&&a<='9'){s=s*10+a-'0',a=getchar();}
    	return s;
    }
    signed main()
    {
    	cin>>n;
    	int u,v,w;
    	for(int i=1;i<n;i++)	
    	{
    		u=read(),v=read(),w=read();
    		add(u,v,w%3);add(v,u,w%3);
    	}	
    	sum=n;mn=INF;
    	getroot(1,0);
    	divide(root);
    	LL fm=n*n;
    	int GCD=gcd(ans,fm);
    	printf("%lld/%lld
    ",ans/GCD,fm/GCD);
    }
    inline void add(int u,int v,int w)
    {
    	++num_e;
    	u(num_e)=u;
    	v(num_e)=v;	
    	w(num_e)=w;
    	n(num_e)=f(u);
    	f(u)=num_e;
    }
    
  • 相关阅读:
    游戏玩家 专有名词 All In One
    Xbox 无线控制器详细使用说明图解教程 All In One
    leetcode online interview All In One
    vcharts custom tooltip All In One
    kaggle All In One
    elpopover ::after style overwrite bug All In One
    webpack 插件 All In One
    js inplace algorithm All In One
    leetcode 面试必刷的算法 100 题 All In One
    vcharts no data All In One
  • 原文地址:https://www.cnblogs.com/Al-Ca/p/11252429.html
Copyright © 2020-2023  润新知