CF1240F Football

Posted purple-wzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1240F Football相关的知识,希望对你有一定的参考价值。

题面

英文题面

题意:有一张(n)(m)边的无向图,有(k)种颜色,你需要对每条边染上这(k)种颜色的其中一种,也可以不染色。需要满足以下限制。

(s_{i,j})表示以(i)为一端的边有多少个被染了颜色(j)。则(forall i,max_{j=1}^k s_{i,j}-min_{j=1}^k s_{i,j} leq 2)

每个点有一个权值(w_i),每染一条((i,j))的边,就会产生(w_i +w_j)的贡献。请输出一种合法染色方案,使得总贡献最大。

(n leq 100,m,k leq 1000)

题解:首先,一定存在一种染色方案,使得所有边都被染色。

我们先考虑一下(k=2)的情况如何进行染色。考虑到如果所有节点的度数都为偶数,我们一定能够找到一条遍历了所有边的回路。因此我们可以试图将原图转化。

  • 新建一个虚拟节点“0”;
  • 对于存在度数为奇数的点的连通块,我们让所有这些点与0号节点连边;
  • 对于所有点度数都为偶数的连通块,我们任意指定一个点,与0号节点连两条边。

不难发现这样构造之后,所有点的度数都是偶数。这样,我们就DFS找到这条回路,然后按照(1212cdots 12)对边进行染色即可,最后将0号节点和与其有关的边删掉就行了。不难发现这样操作过后,每个点的(max-min leq 1)

那么对于(k>2)的情况,我们可以先将所有边随机染色,然后不断判断每个点的染色情况是否合法。如果有不合法的,就拿出(max)(min)对应的这两种颜色,将图中这些边重新染色即可。可以发现这样操作并不会使任何点的(max-min)增加。

(下面引用了大神Um_nik的复杂度证明。)

我们设$P(Coloring)= sum_i sum_j s_{i,j}^2 (()s_{i,j}$的定义见题目描述)。那么有(0 leq P(Coloring) leq 2nm)。每做一次操作,(P(Coloring))是严格递减的。因此,操作次数的上界是(2nm)次,一次操作的复杂度是(O(n+m)),那么总复杂度就是(O(nm(n+m)))

但由于我们对所有边进行了随机化染色,因此,对于一个度数为(d)的节点,它对(P)的期望贡献是(d+d(d-1)/k=d^2/k+d(1-frac{1}{k})),可以近似看作是(d^2/k)。因此,期望操作次数是(O(m))级别的,可以通过本题。

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch==‘-‘)g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
const int INF=1e9+7;
typedef pair<int,int>pii;
vector<pii>e[110];
struct E{
	int to,nt,id;
}t[20200];
#define T t[k].to
int n,m,k,x,clr,tot,v[110],vis[1010],head[110],du[110],a[1010],b[1010],c[1010],cnt[1010];
stack<int>s;
I add(int x,int y,int id){
	t[++tot].to=y;t[tot].nt=head[x];head[x]=tot;t[tot].id=id;du[y]++;
}
I D_1(int x){
	v[x]=1;
	for(re k=head[x];k!=-1;k=t[k].nt){
		if(!v[T])D_1(T);
	}
}
I D_2(int x,int id){
	for(re &k=head[x];k!=-1;k=t[k].nt){
		if(!vis[k>>1]){
			vis[k>>1]=1;
			D_2(T,t[k].id);
		}
	}
	s.emplace(id);
}
I recolor(int A,int B){
	C(head,-1);tot=-1;C(v,0);C(du,0);
	F(i,1,m)if(c[i]==A||c[i]==B)add(a[i],b[i],i),add(b[i],a[i],i);
	F(i,1,n){
		if(du[i]&1){
			add(i,0,0);add(0,i,0);
			if(!v[i])D_1(i);
		}
	}
	F(i,1,n){
		if(v[i]||(du[i]&1))continue;
		D_1(i);
		add(0,i,0);add(i,0,0);
		add(0,i,0);add(i,0,0);
	}
	F(i,0,tot>>1)vis[i]=0;
	D_2(0,0);clr=0;
	while(!s.empty())c[s.top()]=(clr?A:B),s.pop(),clr^=1;
}
int main(){
	srand((unsigned)time(0)+19260817);
	read(n);read(m);read(k);
	F(i,1,n)read(x);
	F(i,1,m)read(a[i]),read(b[i]),e[a[i]].emplace_back(make_pair(b[i],i)),e[b[i]].emplace_back(make_pair(a[i],i)),c[i]=rand()%k+1;
	while(1){
//		cout<<"!";
		re sn=1,mx,mn,px,pn;
		F(i,1,n){
			mx=0;px=pn=1;mn=INF;F(j,1,k)cnt[j]=0;
			for(auto d:e[i])cnt[c[d.second]]++;
			F(j,1,k){
				if(cnt[j]>mx)mx=cnt[j],px=j;
				if(cnt[j]<mn)mn=cnt[j],pn=j;
			}
			if(mx-mn>2)recolor(px,pn),sn=0;
		}
		if(sn)break;
	}
	F(i,1,m)printf("%d
",c[i]);
	return 0;
}

以上是关于CF1240F Football的主要内容,如果未能解决你的问题,请参考以下文章

CF 1244 C - The Football Season

如何从后台弹出片段

POJ 3071 Football

大连网络赛 1006 Football Games

POJ 3071 Football (概率DP)

POJ3071(Football)--概率DP