6389. 【NOIP2019模拟2019.10.26】小w学图论

题目

题目大意

给你一个图,你要自己生成一个新的图,满足对于任意\(x<y<z\)\((x,y)\in E\)\((x,z) \in E\),也有\((y,z) \in E\)
\(n\)种不同的颜色给点染色,问边相连的两个点的颜色互不相同的染色方案数。


思考历程

几乎没有想到什么啊……
开始时觉得这个模型比较难以转化,也没有想到什么比较好的方法。
而且也不会求方案数。
于是直接打了个纯暴力。
后来打了个对拍,证明了题目给的那个伪代码中的while(1)是没有必要的。
于是优化了一点点,然而并没有什么卵用。


正解

其实正解很简单。
首先假设已经把整张图建出来了。
\(g_i\)\(i\)连向大于\(i\)的点的边数。
\((n-g_i)\)乘起来就是答案。
可以从后往前考虑。对于一个点,它要和它连向的点不相同,而且它连向的点的颜色都互不相同,所以就有\((n-g_i)\)的方案数。
然后就是一道水题了。从前往后做,维护连出去的边。在搞完一个点之后将这个边集复制到它连向的最小的点的边集。
很显然这是对的。
复制的时候可以用线段树合并或启发式合并。(我居然没有想过)


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 200010
#define mo 998244353
#define ll long long
int n,m;
struct Node{
	Node *l,*r;
	int num;
} d[N*20],*null;
int cnt;
inline Node *newnode(){return &(d[++cnt]={null,null,0});}
void insert(Node *&t,int l,int r,int x){
	if (t==null)
		t=newnode();
	if (l==r){
		t->num=1;
		return;	
	}
	int mid=l+r>>1;
	if (x<=mid)
		insert(t->l,l,mid,x);
	else
		insert(t->r,mid+1,r,x);
	t->num=t->l->num+t->r->num;
}
int find(Node *t,int l,int r){
	if (t==null)
		return 0;
	if (l==r)
		return l;
	return t->l->num?find(t->l,l,l+r>>1):find(t->r,(l+r>>1)+1,r);
}
Node *merge(Node *a,Node *b,int l,int r,int x){
	if (r<=x)
		return null;
	if (a==null)
		return b;
	if (b==null && x<l)
		return a;
	if (l==r){
		a->num=1;
		return a;
	}
	int mid=l+r>>1;
//	if (mid<=x)
//		a->l=null;
	a->l=merge(a->l,b->l,l,mid,x);
	a->r=merge(a->r,b->r,mid+1,r,x);
	a->num=a->l->num+a->r->num;
	return a;
}
Node *rt[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("test.txt","w",stdout);
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	scanf("%d%d",&n,&m);
	null=new Node;
	*null={null,null,0};
	for (int i=1;i<=n;++i)
		rt[i]=newnode();
	for (int i=1;i<=m;++i){
		int u,v;
		scanf("%d%d",&u,&v);
		if (u>v)
			swap(u,v);
		insert(rt[u],1,n,v);
	}
	ll ans=1;
	for (int i=1;i<=n;++i){
//		printf("%d\n",rt[i]->num);
		ans=ans*(n-rt[i]->num)%mo;
		int x=find(rt[i],1,n);
		if (x)
			rt[x]=merge(rt[i],rt[x],1,n,x);
	}
	printf("%lld\n",ans);
	return 0;
}

总结

我居然连这么水的题目都没有想出来……
我太菜了……

posted @ 2019-10-26 16:59  jz_597  阅读(239)  评论(0编辑  收藏  举报