zoukankan      html  css  js  c++  java
  • Touch

    题目描述

    一句话题意: 求一棵N-2条边权确定的树中那条不确定的边的边权分别为[L,R]中的每一个数时树中路径权值gcd为1的点对有多少。

    (路径的gcd为所有边权的gcd)

    输入格式

    第一行N,L,R。

    第二行是不确定的边的两个端点。

    接下来N-2行每行三个数表示一条边的两个端点和边权。

    输出格式

    L-R+1行,第i行为当不确定边权等于L+i-1时的答案。

    样例

    sample.in:

    5 1 5

    1 2

    2 3 6

    2 4 4

    1 5 3

    sample.out:

    6

    3

    2

    3

    5

    数据范围与提示

    对于 30% 的数据, 满足 N ≤ 1000, R − L ≤ 1000;

    对于另外的 30% 的数据, 满足 N ≤ 10^5, L = R;

    对于 100% 的数据, 满足 N ≤ 10^5, Di ≤ 10^5, 1 ≤ L ≤ R ≤ 10^5.

    注意:版权归杨乐所属!!

    清北寒假省选班的最后一次考试的压轴题,当时我远程提交(厚颜无耻)了个暴力竟然得了80分!!!震惊!

    我的暴力就是维护一个点到子树中的每种gcd路径的数量,然后合并,计算答案。

    所以先贴一个我的暴力(沉迷重载运算符无法自拔)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #define ll long long
    #define maxn 150005
    #define pb push_back
    using namespace std;
    ll ans=0,base=0;
    int to[maxn*2],ne[maxn*2],val[maxn*2];
    int hd[maxn],u,v,n,m,L,R;
    int vis[maxn],dfn,pos[maxn];
    
    int gcd(int a,int b){
    	return b?gcd(b,a%b):a;
    }
    
    struct p{
    	int num,sum;
    };
    
    struct node{
    	vector<p> g;
    	
    	node operator *(const int &u)const{
    		node r; p x;
    		r.g.clear();
    		dfn++;
    		
    		for(int i=g.size()-1;i>=0;i--){
    			x=g[i];
    		    int d=gcd(u,x.num);
    		    if(vis[d]!=dfn){
    		    	vis[d]=dfn,pos[d]=r.g.size();
    		    	r.g.pb((p){d,x.sum});
    			}
    			else{
    				r.g[pos[d]].sum+=x.sum;
    			}
    		}
    		
    		return r;
    	}
    	
    	node operator +(const node &u){
    		node r; p x;
    		r.g.clear();
    		dfn++;
    		
    		for(int i=g.size()-1;i>=0;i--){
    			x=g[i];
    			if(vis[x.num]!=dfn){
    				vis[x.num]=dfn,pos[x.num]=r.g.size();
    				r.g.pb(x);
    			}
    			else r.g[pos[x.num]].sum+=x.sum;
    		}
    		
    		for(int i=u.g.size()-1;i>=0;i--){
    			x=u.g[i];
    			if(vis[x.num]!=dfn){
    				vis[x.num]=dfn,pos[x.num]=r.g.size();
    				r.g.pb(x);
    			}
    			else r.g[pos[x.num]].sum+=x.sum;
    		}
    		
    		return r;
    	}
    }tree[maxn];
    
    inline ll calc(node x,node y){
    	ll an=0;
    	p o,k;
    	for(int i=x.g.size()-1;i>=0;i--){
    		o=x.g[i];
    		for(int j=y.g.size()-1;j>=0;j--){
    			k=y.g[j];
    			if(gcd(k.num,o.num)==1) an+=k.sum*(ll)o.sum;
    		}
    	}
    	
    	return an;
    }
    
    inline ll calc_line(node x,node y,int D){
    	ll an=0;
    	p o,k;
    	for(int i=x.g.size()-1;i>=0;i--){
    		o=x.g[i];
    		for(int j=y.g.size()-1;j>=0;j--){
    			k=y.g[j];
    			if(gcd(gcd(k.num,o.num),D)==1) an+=k.sum*(ll)o.sum;
    		}
    	}
    	
    	return an;
    }
    
    void dfs(int x,int fa){
    	tree[x].g.pb((p){0,1});
    	node w;
    	
    	for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){
    		dfs(to[i],x);
    		w=tree[to[i]]*val[i];
    		ans+=calc(tree[x],w);
    		tree[x]=tree[x]+w;
    	}
    }
    
    int main(){
    	freopen("touch.in","r",stdin);
    	freopen("touch.out","w",stdout);
    	
    	scanf("%d%d%d",&n,&L,&R);
    	scanf("%d%d",&u,&v);
    	int uu,vv,ww;
    	for(int i=n-2;i;i--){
    		scanf("%d%d%d",&uu,&vv,&ww);
    		to[i]=vv,ne[i]=hd[uu],hd[uu]=i,val[i]=ww;
    		i+=n;
    		to[i]=uu,ne[i]=hd[vv],hd[vv]=i,val[i]=ww;		
    		i-=n;
    	}
    	
    	dfs(u,u),dfs(v,v);
    
    	for(int i=L;i<=R;i++) printf("%lld
    ",ans+calc_line(tree[u],tree[v],i));
    	
    	return 0;
    }
    

      

    然后来正经的讲一下正解咳咳。

    (要不是当时T2是我第一次写高精调了大半年最后没时间了我觉得我是能想到的hhh)

    设f[x]为路径gcd为x的点对对数,g[x]为路径gcd为x的倍数的点对对数。

    显然g[x]=Σf[x*i]

    那么反演一下,f[x]=Σg[x*i]*μ[i]

    然后我们求的是f[1],所以答案就是Σg[i]*μ[i] (先假装没有那条边权不定的边)。

    求g[x]很简单,并查集一下就好了。

    那么怎么考虑那条边权不确定的边呢???

    设那条不确定的边的边权是w。

    它只会对d|w的g[d]产生影响,而且影响也是很好计算的,就在计算g[d]之后的并查集上再连一下不定边的两个端点即可。

    至于计算所有g[d]和考虑所有d对d|w的w的影响的话,可以用调和级数开开心心O(N log N)过的。。。(我是不会告诉你标程是N sqrt(N)然后我艹爆了标程hhh,

    其实是老师懒懒得写调和级数)

    这样的话其实时限1s就够了(当然如果时限1s我当时就没法水那么多暴力分了hhh)

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<vector>
    #define ll long long
    #define maxn 100005
    #define pb push_back
    using namespace std;
    vector<int> g[maxn];
    int zs[maxn],t=0,miu[maxn];
    bool v[maxn];
    
    inline void init(){
    	miu[1]=1;
    	for(int i=2;i<=100000;i++){
    		if(!v[i]) zs[++t]=i,miu[i]=-1;
    		for(int j=1,u;j<=t&&(u=zs[j]*i)<=100000;j++){
    			v[u]=1;
    			if(!(i%zs[j])) break;
    			miu[u]=-miu[i];
    		}
    	}
    }
    
    int num,op[maxn],mx;
    int vis[maxn],dfn=0;
    struct lines{
    	int u,v;
    }l[maxn];
    int n,m,S,T,L,R;
    int p[maxn],siz[maxn];
    ll base=0,ans[maxn];
    
    int ff(int x){
    	return p[x]==x?x:(p[x]=ff(p[x]));
    }
    
    inline void clear(){
    	for(int i=1;i<=num;i++){
    		p[op[i]]=op[i];
    		siz[op[i]]=1;
    	}
    	p[S]=S,p[T]=T;
    	siz[S]=siz[T]=1;
    	num=0;
    }
    
    inline ll solve(int x){
    	dfn++;
    	lines e;
    	int fa,fb;
    	ll an=0;
    	for(int i=x;i<=mx;i+=x)
    	    for(int j=g[i].size()-1;j>=0;j--){
    	    	e=l[g[i][j]];
    	    	fa=ff(e.u),fb=ff(e.v);
    	    	if(vis[e.u]!=dfn){
    	    		vis[e.u]=dfn,op[++num]=e.u;
    			}
    	    	if(vis[e.v]!=dfn){
    	    		vis[e.v]=dfn,op[++num]=e.v;
    			}
    			
    			p[fa]=fb,an+=siz[fa]*(ll)siz[fb];
    			siz[fb]+=siz[fa];		
    		}
    		
    	return an;
    }
    
    int main(){
    	init();
    	scanf("%d%d%d",&n,&L,&R);
    	scanf("%d%d",&S,&T);
    	int ww;
    	for(int i=n-2;i;i--){
    		scanf("%d%d%d",&l[i].u,&l[i].v,&ww);
    		g[ww].pb(i),mx=max(mx,ww);
    	}
    	
    	for(int i=1;i<=n;i++) p[i]=i,siz[i]=1;
    	
    	for(int i=mx;i;i--){
    		clear();
    		base+=miu[i]*solve(i);
    
    		int fa=ff(S),fb=ff(T);
    		ll now=siz[fa]*(ll)siz[fb]*(ll)miu[i];
    		
    		for(int j=i;j<=mx;j+=i) ans[j]+=now;
    	}
    
    	for(int i=L;i<=R;i++) printf("%lld
    ",base+ans[i]);
    	return 0;
    }
    

      

    但是暴力还是在loj上A了,对比一下(根据数据的随机性的话,我这种暴力的效果也是很好的):

  • 相关阅读:
    markdown
    线段树模板
    Trie模板 UVALive 3942 Remember the Word
    使用swift写sprite Kit的模仿微信打飞机游戏
    Graffiti support page
    使用代码控制ScrollView的contentSize
    资料整理
    pd.to_sql()用法
    如何将表格的数据导入到mysql
    安装启动MySQL8.0,报错:1053
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8446191.html
Copyright © 2011-2022 走看看