题解 色
自闭了,赛时 \(O(q\sum \deg(x_i))\) 过的然后是最优解
首先有一种人均想到的根号分治但我想偏了给边定完向之后不知道该干啥了
度数小于根号的点暴力
度数大于根号的点每个对每种颜色维护一个桶
桶里存所有连接与它相邻的这个颜色的点的边权
修改时更新所有与它相邻的度数大于根号的点的桶
同时顺便维护一下答案即可
然后正解:
首先发现只有最小生成树上的边有用
- 对于图上的最短路/最小距离之类的问题尝试猜测结论「与最小生成树相关」?
然后这个问题就变成了一个树上的问题
那么树的性质是每个节点只有一个父亲
所以我们在每个节点维护它每种颜色的所有儿子的集合
同时维护一个全局集合为「每个节点与其儿子的异色最小边集合」
修改一个点时更新其父节点的集合及异色最小边,删除其原来的异色最小边并插入新的即可
易得复杂度为 \(O(q\log n)\)
点击查看代码(暴力枚举出边的~~最优解~~Code)
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 300010
#define fir first
#define sec second
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, c, q;
int head[N], col[N], ecnt=1;
struct edge{int to, next, val;}e[N<<1];
inline void add(int s, int t, int w) {e[++ecnt]={t, head[s], w}; head[s]=ecnt;}
namespace force{
bool del[N], inq[N];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q2;
void solve() {
for (int u=1; u<=n; ++u) {
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (u>v) continue;
if (col[u]==col[v]) del[i>>1]=1;
else q2.push({e[i].val, i>>1}), inq[i>>1]=1;
}
}
for (int i=1,x,y; i<=q; ++i) {
x=read(); y=read();
col[x]=y;
for (int i=head[x],v; ~i; i=e[i].next) {
v = e[i].to;
if (col[x]==col[v]) del[i>>1]=1;
else {
del[i>>1]=0;
if (!inq[i>>1]) q2.push({e[i].val, i>>1}), inq[i>>1]=1;
}
}
while (1) {
pair<int, int> u=q2.top();
if (del[u.sec]) q2.pop(), inq[u.sec]=0;
else {
printf("%d\n", u.fir);
break;
}
}
}
}
}
signed main()
{
freopen("color.in", "r", stdin);
freopen("color.out", "w", stdout);
n=read(); m=read(); c=read(); q=read();
memset(head, -1, sizeof(head));
for (int i=1,u,v,w; i<=m; ++i) {
u=read(); v=read(); w=read();
add(u, v, w); add(v, u, w);
}
for (int i=1; i<=n; ++i) col[i]=read();
force::solve();
return 0;
}