zoukankan      html  css  js  c++  java
  • BZOJ.3498.[PA2009]Cakes(三元环 枚举)

    题目链接

    感觉我可能学的假的(复杂度没问题,但是常数巨大)。
    一个比较真的说明见这儿:https://czyhe.me/blog/algorithm/3-mem-ring/3-mem-ring/。


    (Description)

    n个点m条边的无向图,每个点有点权。对于任意一个三元环((i,j,k),i<j<k),其贡献为(max{a_i,a_j,a_k})。求所有三元环的贡献和。


    一般的三元环计数问题:根据出度是否(leqsqrt m)将点分为两类。
    对于(dgr[x]leq sqrt{m})的点x,枚举x的任意两条出边,判断这两条出边的端点间是否有边(map/set/邻接表hash)。因为一条边最多被起点枚举(sqrt m)次,最多枚举(m)条边,所以复杂度不超过(O(msqrt{m}))(无向图的话计算过的点不再计算了)。
    对于(dgr[x]>sqrt{m})的点x,三个三个枚举这些点,暴力判断之间是否有边。因为最多只有(sqrt m)个这样的点,so复杂度是(O((sqrt m)^3)=O(msqrt m))


    (Solution1)

    对于本题,我们消除取max的限制。对于原图中的一条边u<->v,若val[u]<val[v],则连边v->u;否则连边u->v。(相等的话一样吧)
    然后我们从val最大的点遍历新图(因为是有向图,so不会重复)。枚举当前点的出边x->v,若(dgr[v]leqsqrt m),则枚举v的出边,判断是否与x相邻;否则枚举x的出边v',判断是否与v相邻。
    判断是判断v是否与v'有连边,而不是v'与v有边!因为若v'度数小会只枚举其出边,比如如果按后者判断,v->v'有边,枚举v时不算答案;而v'是枚举其出边,并不包括v。这个贡献就没了。so都统一计出边答案就行了。
    (可以在此之前枚举x的出边x->v,对v打标记,就可以知道某个点是否与x相邻)
    复杂度不知道。

    ???卡数组邻接表还行,T了三遍。

    //13948kb	5904ms
    #include <set>
    #include <cmath>
    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 250000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=1e5+5,M=250005;
    
    int n,m,/*Enum,H[N],nxt[M],to[M],*/Link[N],dgr[N];
    std::set<int> s[N];
    std::vector<int> vec[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    struct Node{
    	int val,id;
    	bool operator <(const Node &a)const{
    		return val>a.val;
    	}
    }A[N];
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AddEdge(int u,int v){
    	++dgr[u], vec[u].push_back(v);
    //	++dgr[u], to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    }
    
    int main()
    {
    	n=read(), m=read();
    	for(int i=1; i<=n; ++i) A[i]=(Node){read(),i};
    	for(int i=1,u,v; i<=m; ++i)
    		u=read(), v=read(), A[u].val>A[v].val?(AddEdge(u,v),s[u].insert(v)):(AddEdge(v,u),s[v].insert(u));//set连双向/单向无所谓了,反正连通是单向的(不过这样快啊)
    	std::sort(A+1,A+1+n);
    	long long ans=0;
    	for(int i=1,x,val,size=sqrt(m); i<=n; ++i)
    	{
    		x=A[i].id, val=A[i].val;
    		const std::vector<int> &X=vec[x];
    		int l=X.size();
    		for(int j=0,l=X.size(); j<l; ++j) Link[X[j]]=x;
    		for(int j=0,l=X.size(),v; j<l; ++j)
    		{
    			v=X[j];
    			if(dgr[v]<=size){
    				for(int k=0,l2=vec[v].size(); k<l2; ++k)
    					if(Link[vec[v][k]]==x) ans+=val;
    			}
    			else{
    				for(int k=0; k<l; ++k)
    					if(s[v].count(X[k])) ans+=val;//not s[to[k]].count(v)!!!
    			}
    		}
    //		for(int j=H[x]; j; j=nxt[j]) Link[to[j]]=x;
    //		for(int j=H[x],v; j; j=nxt[j])
    //		{
    //			v=to[j];
    //			if(dgr[v]<=size){
    //				for(int k=H[v]; k; k=nxt[k])
    //					if(Link[to[k]]==x) ans+=val;
    //			}
    //			else{
    //				for(int k=H[x]; k; k=nxt[k])
    //					if(s[v].count(to[k])) ans+=val;//if(s[to[k]].count(v)) ans+=val;//WA
    //			}
    //		}
    	}
    	printf("%lld",ans);
    
    	return 0;
    }
    

    (Solution2)

    给定(n)个点(m)条边的图,对于每一条边,枚举两个端点中度数较少的端点的邻接点,判断是否构成三元环。这种做法的判断次数在(m)条边形成一个完全图时达到最大,即(O(msqrt m))http://tieba.baidu.com/p/4524107534
    对于任意两条边,我们可以直接枚举度数较小的。
    然而这样写怎么TLE了。。本身复杂度不小加每次一定带个set的log?

    //TLE
    #include <set>
    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    //#define gc() getchar()
    #define MAXIN 250000
    #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
    const int N=1e5+5,M=250005;
    
    int n,m,val[N],Link[N],dgr[N];
    std::set<int> s[N];
    std::vector<int> vec[N];
    char IN[MAXIN],*SS=IN,*TT=IN;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    inline void AddEdge(int u,int v){
    	++dgr[u], vec[u].push_back(v), s[u].insert(v);
    }
    long long Calc(int x,int y)
    {
    	const std::vector<int> &X=vec[x];
    	long long res=0; int v=std::max(val[x],val[y]);
    	for(int i=0,l=X.size(); i<l; ++i) if(s[y].count(X[i])) res+=std::max(v,val[X[i]]);
    	return res;
    }
    
    int main()
    {
    	n=read(), m=read();
    	for(int i=1; i<=n; ++i) val[i]=read();
    	for(int i=1,u,v; i<=m; ++i)
    		u=read(), v=read(), val[u]>val[v]?AddEdge(u,v):AddEdge(v,u);//不建有向图的话答案/3就行了 
    	long long ans=0;
    	for(int x=1; x<=n; ++x)
    	{
    		const std::vector<int> &X=vec[x];
    		for(int j=0,l=X.size(); j<l; ++j)
    			ans+=dgr[x]<dgr[X[j]]?Calc(x,X[j]):Calc(X[j],x);
    	}
    	printf("%lld",ans);
    
    	return 0;
    }
    
  • 相关阅读:
    http://blog.csdn.net/steveguoshao/article/details/38414145
    http://www.tuicool.com/articles/EjMJNz
    http://jingyan.baidu.com/article/7f41ecec1b7a2e593d095ce6.html
    Linux 查看当前时间和修改系统时间
    http://m.blog.csdn.net/article/details?id=49132747
    http://www.cnblogs.com/nick-huang/p/4848843.html
    javaScript事件(一)事件流
    jQuery选择器
    超链接a的target属性
    html基础总结版
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9306259.html
Copyright © 2011-2022 走看看