图 解题报告
图
题意:对一个联通无向图,每次找到一个点标号\(a<b<c\)的三元组满足\((a,b),(a,c)\)有边,然后连接\((b,c)\),直到最后找不到三元组,问最终得到的图的\(n\)种颜色染色方案数。
计数问题有一个常见的思路是想办法把每个问题独立出来,考虑如何计算每个点的贡献能够独立的计算。
注意到有一个标号的条件看起来很关键,于是我们可以想办法利用一下。
如果先把图建出来,每个点与与它直接相连的大于它的点颜色一定不同,那么这个点的染色方案是n-与它直接相连大于它的点的个数,并且我们发现这个确实也是独立的,用乘法原理合并一下就可以了。
然后场上没几个人找到这个结论...
考虑我们统计的实际上是每个点向右边连的度数。
可以按标号从小到大扫描,维护每个联通块向当前位置右边的点连边的个数,那么当前位置的答案就是它所在联通块向右边点连边的个数。
实现的时候直接线段树启发式合并就可以了
为了减少常数,每个点合并到它右边的最左边的点的线段树上就可以了。
Code:
#include <cstdio>
#include <cctype>
#include <algorithm>
namespace io
{
const int SIZE=(1<<21)+1;
char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];
int f,qr;
// getchar
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
// print the remaining part
inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
// putchar
inline void putc(char x){*oS++=x;if(oS==oT)flush();}
// input a signed integer
template <class I>
inline void read(I &x)
{
for(f=1,c=gc();c<'0'||c>'9';c=gc()) if(c=='-') f=-1;
for(x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15);x*=f;
}
// print a signed integer
template <class I>
inline void print(I &x)
{
if(!x)putc('0');if(x<0) putc('-'),x=-x;
while(x)qu[++qr]=x%10+'0',x/=10;
while(qr)putc(qu[qr--]);
}
//no need to call flush at the end manually
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io::read;
using io::putc;
using io::print;
const int N=1e6+10;
const int mod=998244353;
int n,m,root[N],sum[N*40],ch[2][N*40],tot;
#define ls ch[0][now]
#define rs ch[1][now]
void ins(int &now,int l,int r,int p)
{
if(!now) now=++tot;
if(l==r) {sum[now]=1;return;}
int mid=l+r>>1;
if(p<=mid) ins(ls,l,mid,p);
else ins(rs,mid+1,r,p);
sum[now]=sum[ls]+sum[rs];
}
void del(int &now,int l,int r,int p)
{
if(l==r) {now=0;return;}
int mid=l+r>>1;
if(p<=mid) del(ls,l,mid,p);
else del(rs,mid+1,r,p);
sum[now]=sum[ls]+sum[rs];
if(!sum[now]) now=0;
}
int qryle(int now,int l,int r)
{
if(l==r) return l;
int mid=l+r>>1;
if(sum[ls]) return qryle(ls,l,mid);
else return qryle(rs,mid+1,r);
}
void Merge(int &now,int v,int l,int r)
{
if(!now||!v) {now=now^v;return;}
if(l==r) return;
int mid=l+r>>1;
Merge(ls,ch[0][v],l,mid);
Merge(rs,ch[1][v],mid+1,r);
sum[now]=sum[ls]+sum[rs];
}
int main()
{
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
read(n),read(m);
for(int u,v,i=1;i<=m;i++)
{
read(u),read(v);
ins(root[u],1,n,v);
}
int ans=1;
for(int i=1;i<=n;i++)
{
ans=1ll*ans*(n-sum[root[i]])%mod;
if(sum[root[i]])
{
int v=qryle(root[i],1,n);
Merge(root[v],root[i],1,n);
del(root[v],1,n,v);
}
}
print(ans);
return 0;
}
2019.3.22