考虑建一张图。
途中\(p_i,q_i\)之间有一条边。
则问题转化成,在每条边上写上两两不同的数字,使得每一条边写的数字和两端的数字不同。
考虑容斥。
钦定一些边,然后给边定向,使得每一个点的出度\(\leq 1\)。
可以发现构成的连通块是个环,直接破环为链dp。
然后对于所有环做一次卷积就是答案。
时间复杂度\(O(N^2\log N)\)。
code:
#include<bits/stdc++.h> #define rb(a,b,c) for(int a=b;a<=c;++a) #define rl(a,b,c) for(int a=b;a>=c;--a) #define LL long long #define IT iterator #define PB push_back #define II(a,b) make_pair(a,b) #define FIR first #define SEC second #define FREO freopen("check.out","w",stdout) #define rep(a,b) for(int a=0;a<b;++a) #define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()) #define random(a) rng()%a #define ALL(a) a.begin(),a.end() #define POB pop_back #define ff fflush(stdout) #define fastio ios::sync_with_stdio(false) #define check_min(a,b) a=min(a,b) #define check_max(a,b) a=max(a,b) using namespace std; const int INF=0x3f3f3f3f; typedef pair<int,int> mp; /*} */ const int MOD=1e9+7; const int MAXN=3003; int fact[MAXN]; int n,fa[MAXN],siz[MAXN]; int root(int x){ return fa[x]=(fa[x]==x? x:root(fa[x])); } void merge(int u,int v){ if(root(u)==root(v)) return ; siz[root(v)]+=siz[root(u)]; fa[root(u)]=root(v); } int dp[2][2][MAXN][MAXN]; void add(int & A,int B){ A+=B; if(A>=MOD) A-=MOD; } vector<int> calc(int len){ if(len==1) return {1,1}; rep(f,2) rb(i,1,len) memset(dp[0][f][i],0,sizeof(dp[0][f][i])),memset(dp[1][f][i],0,sizeof(dp[1][f][i])); dp[0][1][2][1]=1; dp[1][0][2][1]=1; dp[0][0][2][0]=1; vector<int> Ret(len+1,0); rb(i,2,len){ rep(j,2) rep(f,2){ rep(k,i) if(dp[j][f][i][k]){ int val=dp[j][f][i][k]; if(i==len){ add(Ret[k],val); if(!j) add(Ret[k+1],val); if(!f) add(Ret[k+1],val); } else{ add(dp[j][0][i+1][k],val); if(!f) add(dp[j][0][i+1][k+1],val); add(dp[j][1][i+1][k+1],val); } } } } return Ret; } vector<int> mul(vector<int> A,vector<int> B){ vector<int> ret(A.size()+B.size()-1,0); rep(i,A.size()) rep(j,B.size()) add(ret[i+j],1ll*A[i]*B[j]%MOD); return ret; } int q[MAXN],p[MAXN]; int main(){ fact[0]=1; rb(i,1,3000) fact[i]=1ll*fact[i-1]*i%MOD; scanf("%d",&n); rb(i,1,n) scanf("%d",&p[i]); rb(i,1,n) scanf("%d",&q[i]); rb(i,1,n) fa[i]=i,siz[i]=1; rb(i,1,n) merge(q[i],p[i]); vector<vector<int> > poly; rb(i,1,n) if(root(i)==i) poly.PB(calc(siz[i])); priority_queue<mp,vector<mp>,greater<mp> > heap; rep(i,poly.size()) heap.push(II(poly[i].size(),i)); while(heap.size()>=2){ int i,j; i=heap.top().SEC; heap.pop(); j=heap.top().SEC; heap.pop(); poly[i]=mul(poly[j],poly[i]); heap.push(II(poly[i].size(),i)); } int i=heap.top().SEC; int ans=0; rep(j,poly[i].size()){ if(j&1) add(ans,MOD-1ll*fact[n-j]%MOD*poly[i][j]%MOD); else add(ans,1ll*fact[n-j]%MOD*poly[i][j]%MOD); } cout<<ans<<endl; return 0; }
很显然是费用流。
但是spfa会被卡。
需要用dijkstra费用流。
方法:令\(h[i]\)表示当前从\(s\)到\(i\)的最短路减去上一次增广前\(s\)到\(i\)的最短路。
然后考虑对\(h\)跑对短路。
边权\(u\rightarrow v,w\)变成\(w+dis[u]-dis[v]\)。可以发现这个总是\(\geq 0\)。
然后就做完了。
code:
#include<bits/stdc++.h> #define rb(a,b,c) for(int a=b;a<=c;++a) #define rl(a,b,c) for(int a=b;a>=c;--a) #define LL long long #define IT iterator #define PB push_back #define II(a,b) make_pair(a,b) #define FIR first #define SEC second #define FREO freopen("check.out","w",stdout) #define rep(a,b) for(int a=0;a<b;++a) #define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()) #define random(a) rng()%a #define ALL(a) a.begin(),a.end() #define POB pop_back #define ff fflush(stdout) #define fastio ios::sync_with_stdio(false) #define check_min(a,b) a=min(a,b) #define check_max(a,b) a=max(a,b) using namespace std; //inline int read(){ // int x=0; // char ch=getchar(); // while(ch<'0'||ch>'9'){ // ch=getchar(); // } // while(ch>='0'&&ch<='9'){ // x=(x<<1)+(x<<3)+(ch^48); // ch=getchar(); // } // return x; //} #define int LL typedef pair<LL,LL> mp; /*} */ const int GRAPH_SIZE= 4e5+10; int s=0,t=GRAPH_SIZE-1; struct EDGE{ int u,v,c,cos; }; vector<EDGE> e; vector<int> each[GRAPH_SIZE]; int maxflow,mincost; int flow[GRAPH_SIZE]; int dis[GRAPH_SIZE],las[GRAPH_SIZE],h[GRAPH_SIZE]; bool fi=0; int cnt=0; bool spfa(){ flow[s]=1e17; if(!fi){ fi=1; fill(dis,dis+GRAPH_SIZE,1e17); dis[s]=0; rb(now,0,cnt*2){ if(dis[now]==1e17) continue; // cerr<<now<<' '<<dis[now]<<endl; for(auto it:each[now]){ int to,f,c; to=e[it].v; f=e[it].c; c=e[it].cos; if(f<=0) continue; assert(to>now); if(dis[to]>dis[now]+c){ dis[to]=dis[now]+c; las[to]=it; flow[to]=min(flow[now],f); } } } } else{ fill(h,h+GRAPH_SIZE,1e17); h[s]=0; priority_queue<mp,vector<mp>,greater<mp> > heap; heap.push(II(0,s)); while(!heap.empty()){ mp Now=heap.top(); heap.pop(); if(Now.FIR!=h[Now.SEC]) continue; int now=Now.SEC; for(auto it:each[now]){ int to,f,c; to=e[it].v; f=e[it].c; c=e[it].cos+dis[now]-dis[to]; if(f<=0) continue; if(h[to]>h[now]+c){ h[to]=h[now]+c; las[to]=it; flow[to]=min(flow[now],f); heap.push(II(h[to],to)); } } } rep(i,GRAPH_SIZE) dis[i]=min((LL)(1e17),dis[i]+h[i]); } // cerr<<dis[t]<<endl; return dis[t]<=1e16; } void KM(){ while(spfa()){ maxflow+=flow[t]; mincost+=dis[t]*flow[t]; // cout<<mincost<<" "<<maxflow<<endl; int now=t; while(now!=s){ e[las[now]].c-=flow[t]; e[las[now]^1].c+=flow[t]; now=e[las[now]].u; } } } void make_edge(int U,int V,int C,int COS){ EDGE tmp; tmp.u=U; tmp.v=V; tmp.c=C; tmp.cos=COS; e.PB(tmp); each[U].PB(e.size()-1); swap(tmp.u,tmp.v); tmp.c=0; tmp.cos=-COS; e.PB(tmp); each[V].PB(e.size()-1); } const int MAXN=2e5+10; int n,m,k; int in[MAXN],out[MAXN]; int belong[MAXN]; vector<int> g[MAXN],rg[MAXN]; int u[MAXN],v[MAXN]; stack<int> sta; bool vis[MAXN]; map<mp,bool> app; int x[MAXN]; void dfs(int now){ vis[now]=1; for(auto it:g[now]) if(!vis[it]) dfs(it); sta.push(now); } void rdfs(int now){ belong[now]=cnt; for(auto it:rg[now]) if(!belong[it]) rdfs(it); } signed main(){ scanf("%lld%lld%lld",&n,&m,&k); rb(i,1,m){ scanf("%lld%lld",&u[i],&v[i]); g[u[i]].PB(v[i]); rg[v[i]].PB(u[i]); } // cout<<g[1].size()<<endl; rb(i,1,n) if(!vis[i]) dfs(i); while(!sta.empty()){ int now=sta.top(); sta.pop(); if(!belong[now]){ ++cnt; rdfs(now); } } // cout<<"!"<<cnt<<endl; rb(i,1,n){ LL xx; scanf("%lld",&xx),x[belong[i]]+=xx; } rb(i,1,cnt) in[i]=i*2-1,out[i]=i*2; make_edge(s,in[belong[1]],k,0); rb(i,1,m){ int U,V; U=belong[u[i]]; V=belong[v[i]]; if(U==0||V==0) continue; if(U!=V&&!app[II(U,V)]){ app[II(U,V)]=1; assert(U<V); // cout<<out[U]<<" "<<in[V]<<endl; assert(out[U]<in[V]); make_edge(out[U],in[V],k,0); } } // cout<<cnt<<endl; rb(i,1,cnt) make_edge(in[i],out[i],1,-x[i]),make_edge(in[i],out[i],k,0); rb(i,1,cnt) make_edge(out[i],t,k,0); KM(); cout<<-mincost<<endl; return 0; }