UOJ226. 【UR #15】奥林匹克环城马拉松 [组合数学,图论]

UOJ

思路

我们知道关于有向图欧拉回路计数有一个结论:在每个点入度等于出度的时候,答案就是

\[t_w(G)\prod (deg_i-1)! \]

其中\(t_w(G)\)是以某个点为根的树形图个数。(必须确定是外向树还是内向树)

(由这个公式,我们可以知道这种图下面以每个点为根的外向树和内向树的个数都是一样的,但我不会证/kk)

还有一个结论,不过这题用不上:对于一个有向图,以点\(x\)为根的树的个数是用度数矩阵减去邻接矩阵,再删去第\(x\)行第\(x\)列,的行列式。其中那个度数矩阵如果求内向树那么就是出度,外向树就是入度。

所以在图是一棵树的时候,显然每组边都会有一半向上一半向下,就可以得到答案。

基环树的时候,其实问题就转化为给环上的边定向。

我们可以枚举某一条边的定向方案,然后就可以推出其他所有边。

然后再求一下树形图个数,就做完了。

代码

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define templ template<typename T>
    #define sz 10101
    #define mod 998244353ll
    typedef long long ll;
    typedef double db;
    mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
    templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
    templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
    templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
    templ inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();double d=0.1;
        while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
        while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
        if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
        t=(f?-t:t);
    }
    template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
    char __sr[1<<21],__z[20];int __C=-1,__zz=0;
    inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
    inline void print(register int x)
    {
        if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
        while(__z[++__zz]=x%10+48,x/=10);
        while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
    }
    void file()
    {
        #ifdef NTFOrz
        freopen("a.in","r",stdin);
        #endif
    }
    inline void chktime()
    {
        #ifndef ONLINE_JUDGE
        cout<<(clock()-t)/1000.0<<'\n';
        #endif
    }
    #ifdef mod
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
    ll inv(ll x){return ksm(x,mod-2);}
    #else
    ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
    #endif
//	inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

ll fac[sz<<10],_fac[sz<<10];
void init()
{
	_fac[0]=fac[0]=1;
	rep(i,1,sz*1e3) fac[i]=fac[i-1]*i%mod;
	_fac[sz*(int)1e3]=inv(fac[sz*(int)1e3]);
	drep(i,sz*1e3-1,1) _fac[i]=_fac[i+1]*(i+1)%mod; 
}
ll C(int n,int m){return n>=m&&m>=0?fac[n]*_fac[m]%mod*_fac[n-m]%mod:0;}

int n,m;
struct hh{int t,nxt,cnt;}edge[sz<<1];
int head[sz],ecnt=1;
int deg[sz];
void make_edge(int f,int t,int cc)
{
	edge[++ecnt]=(hh){t,head[f],cc};
	head[f]=ecnt;
	edge[++ecnt]=(hh){f,head[t],cc};
	head[t]=ecnt;
	deg[f]+=cc,deg[t]+=cc;
}

namespace Tree
{
	int solve()
	{
		ll ans=1;
		rep(i,1,n) ans=ans*fac[deg[i]/2-1]%mod;
		rep(i,1,n-1) ans=ans*C(edge[i<<1].cnt,edge[i<<1].cnt/2)%mod*edge[i<<1].cnt/2%mod;
		cout<<ans;
		return 0;
	}
}

namespace WTF
{
	ll prod=1;
	int incir[sz];
	vector<pii>cir; // fir: the point in the circle; sec: the edge that connects itself and the previous one
	int vis[sz],in[sz];
	#define v edge[i].t
	int dfs(int x,int fa)
	{
		int ret=0,id=0;
		in[x]=vis[x]=1;
		go(x) if (v!=fa)
		{
			if (!vis[v])
			{
				int cur=dfs(v,x);
				if (!cur) { prod=prod*C(edge[i].cnt,edge[i].cnt/2)%mod*edge[i].cnt/2%mod; continue; }
				ret=cur;id=i;
			}
			else if (in[v]) ret=v,id=i;
		}
		if (ret) cir.push_back(MP(x,id)),incir[x]=1;
		if (ret==x) ret=0;
		prod=prod*fac[deg[x]/2-1]%mod;
		in[x]=0;
		return ret;
	}
	#undef v
	int L[sz],R[sz]; // 出度 
	int L_[sz],R_[sz]; // 入度
	ll pre[sz],suf[sz];
	int solve()
	{
		dfs(1,0);
		int m=(int)cir.size()-1;
		ll ans=0;
		rep(k,0,edge[cir[0].sec].cnt) 
		{
			R_[m]=L[0]=k;R[m]=L_[0]=edge[cir[0].sec].cnt-k;
			rep(i,0,m-1) 
			{
				int delta=L[i]-L_[i],cnt=edge[cir[i+1].sec].cnt;
				L_[i+1]=R[i]=(cnt-delta)/2;L[i+1]=R_[i]=(cnt+delta)/2;
			}
			bool flg=1;
			rep(i,0,m) if (L[i]<0||R[i]<0) flg=0;
			if (!flg) continue;
			ll cur=prod;
			rep(i,0,m) cur=cur*C(edge[cir[i].sec].cnt,L[i])%mod;
			pre[0]=1;rep(i,1,m) pre[i]=pre[i-1]*L[i]%mod;
			suf[m+1]=1;drep(i,m,1) suf[i]=suf[i+1]*R[i]%mod;
			rep(i,0,m) (ans+=cur*pre[i]%mod*suf[i+1]%mod)%=mod;
		}
		cout<<ans;
		return 0;
	}
}

int main()
{
    file();
    init();
	read(n,m);
	int x,y,z;
	rep(i,1,m) read(x,y,z),make_edge(x,y,z);
	rep(i,1,n) if (deg[i]&1) return puts("0"),0;
	if (m==n-1) return Tree::solve();
	return WTF::solve();
}
posted @ 2019-08-12 20:30  p_b_p_b  阅读(271)  评论(0编辑  收藏  举报