7311. 【2021.10.18NOIP提高组模拟】葡萄庄园
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;
}