• JZOJ5803.【2018.8.12省选模拟】girls(三元环)


    PROBLEM

    有0至n-1个元素,给出m对元素的冲突,给出A,B,C,定义一个满足i<j<k的三元组(i,j,k)的贡献为A∗i+B∗j+C∗k,求所有没有冲突的三元组的贡献和

    SOLUTION

    考虑容斥,设F(i)表示每个三元组考虑i组冲突的总和

    Ans=F(0)−F(1)+F(2)−F(3)

    没有冲突的每个元素分别为A,B,C时独立计算次数

    一条冲突的枚举冲突,讨论这两个端点的前后情况

    两条冲突的同理同样是讨论。

    考虑三条冲突在一个三元组里面,如果把每一个冲突看成一个环的话,那么就是对每一个三元环进行计数。问题在于三元环。

    有两种方法。

    1. 第一种:将点根据度数是否小于sqrt(m)分成两类,小于sqrt(m)的点将其所有的连边暴力,时间为sqrt(m)^ 3;对于另一类点,直接三次方暴力只在这一类里的点,由于这里的点数不会超过sqrt(m),所以总的复杂度也不会超过sqrt(m)^ 3.因此总的复杂度不超过sqrt(m)^3。但由于是双向边,常数巨大。

    2. 第二种更优的做法: 将无向边定向为有向边,根据度数小的点连向度数大的点、其次是编号小的点连向编号大的点,可以发现这是一个有向无环图,直接枚举点,暴力枚举两条边并判断这两个点是否联通(hash、set或map)即可。并且这种方法不用去重,因为有向边只能从一个点出发计算,所以十分合适这题的打法,真是妙啊妙。

      考虑证明这种做法的正确性与时间复杂度。

      如果有一个长度为3的环,是不能够从一个点扫描到的。由于连向的点的度数不减,那么回到自己后度数不变,即说明这个环的度数相等,但我们又有编号小的点连向编号大的点,所以在这种情况下一定不会有环。

      长度为3的非环,所以有两条边共了一个起点,可以考虑到这种情况。

      因为每个点连向的点的度数比它大,所以度数不超过sqrt(n)。均摊复杂度不会超过m*sqrt(n),并不会证这种方法QwQ

    3. 还有另一种枚举的方法,时间复杂度可证:

      枚举点,将其通往的所有点打标记,再枚举通往的点,再暴力枚举这个点的出边是否有通往已标记的点上的。打标记为O(m),枚举每个通往的点总数为边数m,再枚举出边sqrt(n),总复杂度O(m*sqrt(n))

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #define maxn 400005
    #define ll unsigned long long
    using namespace std;
    
    int n,m,x,y,z,du[maxn],a[maxn][2],tot,node[maxn];
    int em,e[maxn],nx[maxn],ls[maxn],r[3];
    ll A,B,C,ans,i,j,k,len1,len2,s;
    ll num[maxn],sum[maxn],pc[maxn],ps[maxn];
    vector<int> to[maxn];
    void insert(int x,int y){em++;e[em]=y;nx[em]=ls[x];ls[x]=em;}
    ll que(ll x){if (x<=0) return 0; else return x*(x+1)/2;}
    
    const ll p1=998244353,p2=10007,mo=3000007;
    int hx[mo],p[mo][2];
    void link(int x,int y){
    	if (x>y) swap(x,y);
    	ll s=(x*p1+y*p2)%mo;
    	while (hx[s]&&(p[s][0]!=x||p[s][1]!=y)) 
    		{s++; if (s==mo) s-=mo;}
    	hx[s]=1,p[s][0]=x,p[s][1]=y;
    }
    int pd(int x,int y){
    	if (x>y) swap(x,y);
    	ll s=(x*p1+y*p2)%mo;
    	while (hx[s]&&(p[s][0]!=x||p[s][1]!=y))
    		{s++; if (s==mo) s-=mo;}
    	return hx[s];
    }
    
    int main(){
    	freopen("girls.in","r",stdin);
    	freopen("girls.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	scanf("%lld%lld%lld",&A,&B,&C);
    	for(i=0;i<n;i++){
    		len1=i,len2=n-i-1;
    		ans+=A*i*((len2-1)*len2/2);
    		ans+=B*i*len1*len2;
    		ans+=C*i*((len1-1)*len1/2);
    	}
    	for(i=1;i<=m;i++) {
    		scanf("%d%d",&x,&y);
    		if (x>y) swap(x,y);
    		a[i][0]=x,a[i][1]=y;
    		num[y]++,sum[y]+=x;
    		to[y].push_back(x);
    		du[x]++,du[y]++;
    		ans-=(A*x+B*y)*(n-y-1)+(que(n-1)-que(y))*C;
    		ans-=(A*x+C*y)*(y-x-1)+(que(y-1)-que(x))*B;
    		ans-=(B*x+C*y)*x+que(x-1)*A;
    	}
    	
        for(i=0;i<n;i++) sort(to[i].begin(),to[i].end());
    	for(i=0;i<n;i++){
    		ans+=num[i]*(num[i]-1)/2*i*C;
    		for(j=0;j<to[i].size();j++){
    			x=to[i][j];
    			ans+=(num[i]-j-1)*to[i][j]*A;
    			ans+=j*to[i][j]*B;
    			
    			ans+=num[to[i][j]]*(B*to[i][j]+C*i);
    			ans+=sum[to[i][j]]*A;
    			
    			ans+=pc[to[i][j]]*(A*to[i][j]+C*i);
    			ans+=ps[to[i][j]]*B;
    			
    			pc[to[i][j]]++,ps[to[i][j]]+=i;
    		}
    	}
    	
    	for(i=1;i<=m;i++){
    		x=a[i][0],y=a[i][1];
    		if (du[x]<du[y]) insert(x,y); else 
    		if (du[x]>du[y]) insert(y,x); else 
    			insert(x,y);
    		link(x,y);
    	}
    	
    	for(x=0;x<n;x++){
    		tot=0;
    		for(i=ls[x];i;i=nx[i]) node[++tot]=e[i];
    		for(i=1;i<tot;i++) for(j=i+1;j<=tot;j++){
    			r[0]=x,r[1]=node[i],r[2]=node[j];
    			if (!pd(r[1],r[2])) continue;
    			sort(r,r+3);
    			ans-=r[0]*A+r[1]*B+r[2]*C;
    		}
    	}
    	printf("%llu",ans);
    }
    
  • 相关阅读:
    react脚手架和JSX
    promise
    防抖和节流
    call/apply/bind 用法
    js this指向
    vue单页面应用刷新网页后vuex的state数据丢失的解决方案
    Echarts基础
    继承
    原型链
    vue项目中使用生成动态二维码
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/11700965.html
Copyright © 2020-2023  润新知