图 解题报告

题意:对一个联通无向图,每次找到一个点标号\(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

posted @ 2019-03-22 17:00  露迭月  阅读(216)  评论(0编辑  收藏  举报