1. 程式人生 > >2018 ACM-ICPC青島賽區 B題 Kawa Exam 題解

2018 ACM-ICPC青島賽區 B題 Kawa Exam 題解

題意:
BaoBao正在進行線上考試(都是選擇題),每個題都有唯一的一個正確答案,但是考試系統有m個bug(就是有m個限制),每個bug表示為第u個問題和第v個問題你必須選擇相同的選項,題目問你,如果你修好了第i個bug,BaoBao最高可以取得多少分。

題目數量1e5
BUG數量1e5(真多)
答案範圍1e5

思路:首先,如果出現了bug,導致{a1,a2,...,an}n個題目必須選擇一樣的結果,那麼最高得分肯定是眾數的出現次數。我們發現bug是具有傳遞性的,如果bug連成了一個環,而且你只修復其中一個bug,那麼這個bug的修復是對整個考試系統是沒有任何影響的,所以可以把這些環摳出來,縮成一個點,具體的縮點方法很多,也可以把整個塊拉成一條鏈或者拓撲排序弄一弄,或者tarjan縮一下,我是用tarjan縮點的,這樣整個圖就是一個樹了。
現在修一個bug就相當於斷了一條邊,一棵樹就被切成兩半了,我們需要同時得到兩邊的眾數的個數是多少,這時候就有一個演算法叫dsu on tree了,這是一個複雜度十分科學的暴力演算法,http://codeforces.com/problemset/problem/600/E 這個題是一個dsu on tree的裸題,就是讓你求每個子樹的眾數,不會這個演算法的可以去學一下,做完這個題你就發現你會做子樹內眾數的個數了,現在還有個問題,子樹外的怎麼求呢?我們可以反過來做,一開始把所有的節點加進貢獻裡面去,然後dsu on tree的時候,詢問子樹眾數的新增刪除的時候,我們只要做相反的操作就行了。

具體細節還挺多的。

 

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn = 100010;struct Edge {
  4     int to,next,id;
  5     Edge(int _to=0,int _next=-1,int _id=0):to(_to),next(_next),id(_id) {};
  6 } edge[maxn*2];
  7 int head[maxn],etot;
  8 inline void addedge(int u,int v,int
id) { 9 edge[++etot]=Edge(v,head[u],id); 10 head[u]=etot; 11 } 12 vector<int> nodes[maxn]; 13 int Cnt; 14 int dfn[maxn],low[maxn],tot; 15 bool Vis[maxn]; 16 int S[maxn],top; 17 int id[maxn]; 18 void tarjan(int x,int fa) { 19 low[x]=dfn[x]=++tot; 20 S[++top]=x; 21 Vis[x]=1
; 22 for(register int i=head[x]; ~i; i=edge[i].next) { 23 int v=edge[i].to; 24 if(v==fa) {fa=0;continue;} 25 if(!dfn[v]) { 26 tarjan(v,x); 27 low[x]=min(low[x],low[v]); 28 } else if(Vis[v]) 29 low[x]=min(low[x],dfn[v]); 30 } 31 if(low[x]==dfn[x]) { 32 Cnt++; 33 while(1) { 34 int now=S[top--]; 35 Vis[now]=0; 36 id[now]=Cnt; 37 nodes[Cnt].push_back(now); 38 if(now==x) break; 39 } 40 } 41 } 42 int a[maxn],ans[maxn]; 43 vector<int>v[maxn]; 44 vector<int>edg[maxn],eid[maxn]; 45 int summ; 46 bool vis[maxn]; 47 int sz[maxn],son[maxn]; 48 vector<int>vv; 49 int getid(int x) { 50 return lower_bound(vv.begin(),vv.end(),x)-vv.begin()+1; 51 } 52 void init(int now,int pre=-1){ 53 vis[now]=1; 54 sz[now]=v[now].size(); 55 for(int it:v[now])vv.push_back(a[it]); 56 for(int to:edg[now]){ 57 if(to==pre)continue; 58 init(to,now); 59 sz[now]+=sz[to]; 60 if(!son[now]||sz[to]>sz[son[now]]) 61 son[now]=to; 62 } 63 } 64 int tp[maxn],tp2[maxn],cnt[maxn],cnt2[maxn],Max=0,Max2=0; 65 bool big[maxn]; 66 void update(int nows,int pre,int val){ 67 for(int now:v[nows]){ 68 tp[cnt[a[now]]]--; 69 cnt[a[now]]+=val; 70 tp[cnt[a[now]]]++; 71 if(cnt[a[now]]>Max) 72 Max=cnt[a[now]]; 73 if(!tp[Max])Max--; 74 tp2[cnt2[a[now]]]--; 75 cnt2[a[now]]-=val; 76 tp2[cnt2[a[now]]]++; 77 if(cnt2[a[now]]>Max2) 78 Max2=cnt2[a[now]]; 79 if(!tp2[Max2])Max2--; 80 } 81 for(int to:edg[nows]) 82 if(to!=pre&&big[to]==0) 83 update(to,nows,val); 84 } 85 void update2(int nows,int pre,int val){ 86 for(int now:v[nows]){ 87 tp2[cnt2[a[now]]]--; 88 cnt2[a[now]]+=val; 89 tp2[cnt2[a[now]]]++; 90 if(cnt2[a[now]]>Max2) 91 Max2=cnt2[a[now]]; 92 if(!tp2[Max2])Max2--; 93 } 94 } 95 int temp; 96 void dfs(int now,int pre=-1,int kep=0,int id=0){ 97 int tid=0; 98 for(register int i=0;i<edg[now].size();i++){ 99 int to=edg[now][i]; 100 if(to==son[now])tid=eid[now][i]; 101 if(to==pre||to==son[now])continue; 102 dfs(to,now,0,eid[now][i]); 103 } 104 if(son[now]) 105 dfs(son[now],now,1,tid),big[son[now]]=1; 106 update(now,pre,1); 107 ans[id]=Max+Max2-temp; 108 big[son[now]]=0; 109 if(!kep)update(now,pre,-1); 110 } 111 void init2(int now,int pre=-1){ 112 for(int it:v[now])a[it]=getid(a[it]); 113 update2(now,0,1); 114 for(int to:edg[now]){ 115 if(to==pre)continue; 116 init2(to,now); 117 } 118 } 119 void solve(int now){ 120 vv.clear(); 121 init(now); 122 for(register int i=0;i<=sz[now];i++)tp[i]=tp2[i]=cnt[i]=cnt2[i]=big[i]=0;Max=Max2=0; 123 sort(vv.begin(),vv.end()); 124 vv.erase(unique(vv.begin(),vv.end()),vv.end()); 125 init2(now); 126 summ+=Max2; 127 temp=Max2; 128 dfs(now); 129 } 130 int main() 131 { 132 int T; 133 scanf("%d",&T); 134 while(T--){ 135 int n,m; 136 scanf("%d%d",&n,&m); 137 for(register int i=1;i<=n;i++)head[i]=-1,dfn[i]=son[i]=vis[i]=Vis[i]=0,v[i].clear(),edg[i].clear(),eid[i].clear(); 138 etot=tot=Cnt=top=0; 139 for(register int i=1;i<=n;i++)scanf("%d",a+i); 140 for(register int i=1;i<=m;i++){ 141 int u,v; 142 scanf("%d%d",&u,&v); 143 addedge(v,u,i); 144 addedge(u,v,i); 145 } 146 for(register int i=1;i<=n;i++)if(!dfn[i])tarjan(i,0); 147 for(register int i=1;i<=n;i++)v[id[i]].push_back(i); 148 summ=0; 149 for(register int i=1;i<=n;i++){ 150 for(register int j=head[i];~j;j=edge[j].next){ 151 int u=id[i],to=id[edge[j].to]; 152 if(u==to){ 153 ans[edge[j].id]=0; 154 continue; 155 } 156 edg[u].push_back(to);eid[u].push_back(edge[j].id); 157 } 158 } 159 for(register int i=1;i<=Cnt;i++) 160 if(!vis[i]) 161 solve(i); 162 for(register int i=1;i<=m;i++){ 163 if(i>1)putchar(' '); 164 printf("%d",ans[i]+summ); 165 } 166 puts(""); 167 } 168 return 0; 169 }