#狀壓dp,拓撲排序,內向基環樹#CF1242C Sum Balance
阿新 • • 發佈:2022-03-25
題目
有 \(k\) 個盒子, 第 \(i\) 個盒子有 \(n_i\) 個數. 保證所有數互不相同。
從每個盒子各拿出一個數, 並按照某種順序放回去(每個盒子恰好放入一個數)。
判斷是否能使操作後所有盒子內的數的和相同, 有解需要輸出任意一個方案。
\(k\leq 15,n_i\leq 5000\)
分析
可以發現拿出一個數,那麼放進去的這個數也是確定的,那麼這樣就確定的有向的關係。
由於每個點只往外連一條邊,所以這張有向圖實則形成一個內向基環樹。
相當於要將所有的環抽出來,可以作為一種子方案,然後再將所有環拼接起來,相當於列舉子集。
時間複雜度為 \(O(3^k+2^k*k)\)
可以先跑一遍拓撲排序將非環的部分剔除掉,然後只找環的部分,注意如果環記憶體在兩個數在同一盒子且並不相同,那麼這個環不能使用。
程式碼
#include <cstdio> #include <cctype> #include <queue> #include <vector> #include <map> #include <algorithm> using namespace std; const int N=80101; vector<int>ans[N]; map<long long,int>dfn; queue<int>q; int xo[N],deg[N],nxt[N],v[N],n,al,a[16][N>>4],S,tot,X[N],Y[N],pre[N]; long long s[16],sum; int iut(){ int ans=0,f=1; char c=getchar(); while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar(); while (isdigit(c)) ans=ans*10+c-48,c=getchar(); return ans*f; } int main(){ n=iut(),al=(1<<n)-1; for (int i=1;i<=al;++i) xo[i]=xo[i&(i-1)]+1; for (int i=1;i<=n;++i){ a[i][0]=iut(); for (int j=1;j<=a[i][0];++j){ a[i][j]=iut(),s[i]+=a[i][j]; dfn[a[i][j]]=++tot; X[tot]=i,Y[tot]=j; } sum+=s[i]; } if (sum%n) return !printf("No"); sum/=n; for (int i=1;i<=n;++i){ for (int j=1;j<=a[i][0];++j) if (dfn.count(sum-(s[i]-a[i][j]))){ int x=dfn[a[i][j]],y=dfn[sum-(s[i]-a[i][j])]; if (X[x]==X[y]&&Y[x]!=Y[y]) continue; nxt[x]=y,++deg[y]; } } for (int i=1;i<=tot;++i) if (!deg[i]) q.push(i); while (!q.empty()){ int x=q.front(); q.pop(); if (nxt[x]&&!(--deg[nxt[x]])) q.push(nxt[x]); } for (int i=1,flag;i<=tot;++i) if (deg[i]&&!v[i]){ S=1<<(X[i]-1),flag=v[i]=1,pre[nxt[i]]=i; for (int x=nxt[i];x!=i;x=nxt[x]){ if ((S>>(X[x]-1))&1) flag=0; pre[nxt[x]]=x,v[x]=1,S|=1<<(X[x]-1); } if (!flag||!ans[S].empty()) continue; ans[S].push_back(i); for (int x=nxt[i];x!=i;x=nxt[x]) ans[S].push_back(x); } for (int i=1;i<=al;++i) if (ans[i].empty()){ for (int j=i&(i-1);j;j=(j-1)&i) if (!ans[j].empty()&&!ans[i^j].empty()){ ans[i]=ans[i^j]; for (int k=0;k<xo[j];++k) ans[i].push_back(ans[j][k]); } } if (ans[al].empty()) return !printf("No"); sort(ans[al].begin(),ans[al].end()); printf("Yes\n"); for (int i=0;i<n;++i){ int now=ans[al][i]; printf("%d %d\n",a[X[now]][Y[now]],X[pre[now]]); } return 0; }