题目
题目大意
给你一个图,你要自己生成一个新的图,满足对于任意(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
",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
",ans);
return 0;
}
总结
我居然连这么水的题目都没有想出来……
我太菜了……