dfs+基環樹上dp--HDU 6403 Card Game
阿新 • • 發佈:2018-12-14
闆闆講的題!
把每張牌看成一條邊,正面向反面連權值為1的邊,反之為0
每個點只能有<=1個入度
可以看出只有樹或者基環樹才是合法的
所以就只要在樹或者基環樹上dp就好了
這個dp可以先定一個根,因為一定有一個點是入度為0的,先求出這個值,然後可以dp求出以其他點為根的值
最後答案取最小的,方案數也可以相應算出
程式碼如下:(沒有壓行的106quq)
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #define maxn 200005 #define LL long long #define inf 0x3f3f3f3f using namespace std; int T,n,cnt,head[maxn],a[maxn],sz,ed,s,t,pos; LL f[maxn],g[maxn],ans,ans2,num; vector<int> vec; const int mod=998244353; bool vis[maxn],flg,cir[maxn]; inline int rd(){ int x=0,f=1;char c=' '; while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar(); while(c<='9' && c>='0') x=x*10+c-'0',c=getchar(); return x*f; } struct EDGE{ int to,nxt,w; }edge[maxn<<1]; inline void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].w=z; head[x]=cnt; } inline void init(){ cnt=1,ans=0,ans2=1; memset(head,0,sizeof head); memset(vis,0,sizeof vis); } inline void calc(int u){ vis[u]=1; sz++; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; ed++; if(!vis[v]) calc(v); } return; } inline void dfs(int u,int fa){ vis[u]=1; f[u]=0; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v==fa) continue; if(!vis[v]) { dfs(v,u); f[u]+=f[v]+edge[i].w; } else s=u,t=v,pos=i; } } inline void DP(int u,int fa){ vec.push_back(g[u]); for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v==fa)continue; if(i==pos || i==(pos^1)) continue; g[v]=g[u]+(edge[i].w?-1:1);//以v為根的話原來是1的邊要-1,是0要+1 DP(v,u); } } int main(){ T=rd(); while(T--){ n=rd(); init(); flg=true; for(int i=1;i<=n;i++){ int x=rd(),y=rd(); add(x,y,1); add(y,x,0); } for(int i=1;i<=2*n;i++) if(!vis[i]){ sz=ed=0; calc(i);//判斷每個聯通塊是不是樹or基環樹 if(ed/2>sz) {flg=false;break;} } if(!flg) {puts("-1 -1");continue;} memset(vis,0,sizeof vis); for(int i=1;i<=2*n;i++) if(!vis[i]){ s=t=pos=-1; num=0; vec.clear(); dfs(i,0); g[i]=f[i];//dfs找到環,首先計算以i為根的值 DP(i,0);//再dp以其他點為根的值 if(s==-1){//無環 sort(vec.begin(),vec.end()); for(int j=0;j<vec.size();j++){ if(vec[j]!=vec[0]) break; num++; } ans+=vec[0]; } else{ pos%=2;//看環上哪個方向值更小 if(g[s]+pos==g[t]+(pos^1)) num=2; else num=1; ans+=min(g[s]+pos,g[t]+(pos^1)); } ans2=ans2*num%mod;//方案數 } printf("%lld %lld\n",ans,ans2); } return 0; }