7311. 【2021.10.18NOIP提高組模擬】葡萄莊園
阿新 • • 發佈:2021-10-20
Description
\(n\le 10^5,m\le 2\times 10^5\)。
Solution
由於顏色只有 3 個,考慮將圖分層。
統計出不走某個顏色的情況下,每個節點所處的聯通塊。
那麼改變顏色可以認為是在某個節點同時走兩個聯通塊。
預處理答案,列舉節點,統計所處的三個聯通塊兩兩聯通的價值,注意去重(可以用 \(\text{map}\),也可以排序)。
同時維護每個連通塊與別的連通塊連線的最優情況。
對於詢問的 \(x\),找出所處的三個連通塊中最優情況價值最大的,即為答案。
Code
#include<cstdio> #include<cstring> #include<algorithm> #define N 100005 #define M 200005 #define ll long long using namespace std; int n,m,q,x,y,z,tot,num,sum,len,cir[N][5]; ll a[N],size[N*3],f[N*3]; struct node { int to,next,head,cl; }edge[M<<1]; struct circ { int id,x,y; ll v; }c[N*3],d[N*3]; void add(int x,int y,int z) { edge[++tot].to=y; edge[tot].cl=z; edge[tot].next=edge[x].head; edge[x].head=tot; } void dfs(int x,int c) { size[num]+=a[x]; for (int i=edge[x].head;i;i=edge[i].next) { int v=edge[i].to; if (edge[i].cl==c) continue; if (cir[v][c]) continue; cir[v][c]=num; dfs(v,c); } } bool cmp(circ x,circ y) { if (x.x<y.x) return true; if (x.x>y.x) return false; return x.y<y.y; } int main() { freopen("grape.in","r",stdin); freopen("grape.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;++i) scanf("%lld",&a[i]); for (int i=1;i<=m;++i) { scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } for(int j=1;j<=3;++j) for (int i=1;i<=n;++i) if (!cir[i][j]) cir[i][j]=++num,dfs(i,j); for (int i=1;i<=n;++i) { x=cir[i][1];y=cir[i][2];z=cir[i][3]; c[++sum].x=x;c[sum].y=y;c[sum].v=size[x]+size[y],c[sum].id=i; c[++sum].x=x;c[sum].y=z;c[sum].v=size[x]+size[z],c[sum].id=i; c[++sum].x=y;c[sum].y=z;c[sum].v=size[y]+size[z],c[sum].id=i; } sort(c+1,c+sum+1,cmp); for (int i=1;i<=sum;++i) { if (c[i].x!=c[i-1].x||c[i].y!=c[i-1].y) { if (len) f[d[len].x]=max(f[d[len].x],d[len].v),f[d[len].y]=max(f[d[len].y],d[len].v); d[++len].x=c[i].x; d[len].y=c[i].y; d[len].v=c[i].v-a[c[i].id]; } else d[len].v-=a[c[i].id]; }//去重並統計答案 scanf("%d",&q); while (q--) { scanf("%d",&x); printf("%lld\n",max(max(f[cir[x][1]],f[cir[x][2]]),f[cir[x][3]])); } return 0; }