题目链接
(luogu上现在这个题时限非常紧,题解区的代码也不一定能过)
题意:
有一张n个点,m条边的无向图,然后每条边有权值;有两种操作:
1 将一条边的权值修改.
2 询问从某个点开始,只允许进过大于给定权值的边,最多可以到达几个点.
解法
首先考虑如果没有修改操作,应该怎么做:
显然可以把询问离线下来,并按给定的权值从大到小排序,然后用并查集维护答案.
如果有操作呢?我们也延续上面的思路,用并查集维护答案,然后就是每
次操作分为一块,对于块内的查询操作,显然未在块内被修改过的边就不会被修改,所以可以直接排序好,用没有修改操作的方法维护,然后对于一次查询,暴力遍历所有修改操作,考虑修改操作的时间顺序,判断是否需要修改.
然后一个块的操作处理完以后,把所有修改操作都处理了,此时边的权值发生了改变,需要重新排序.
注意
这个题写了我一下午,主要是在块内的修改操作上一直有细节没有解决:一条边可能被修改了多次,只有最近的一次才是有用的.然后如果某条边在查询前就被修改过,查询后又被修改,还是只能用查询前的(可能不太好理解,可以看代码).
然后排序不能每次直接暴力排m条边,而是只排被修改的边,然后合并回去的时候需要归并.所以一开始边要是有序的(一开始需要排一次序).
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
inline int read(){
char c=getchar();int t=0,f=1;
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int n,m,Q;
struct node{
int u,v,w,id;
node(int a=0,int b=0,int c=0,int d=0){u=a,v=b,w=c,id=d;}
}e[maxn],e1[maxn],e2[maxn];
struct ak{
int op,s,w,id;
ak(int a=0,int b=0,int c=0,int d=0){op=a,s=b,w=c;id=d;}
}q[maxn],alfa[maxn],beta[maxn];
int cnt;
bool cmp(node a,node b){
return a.w>b.w;
}
bool cmp2(ak a,ak b){
return a.w>b.w;
}
int op[maxn],f[maxn];
inline int find(int x){
return f[x]==x?f[x]:find(f[x]);
}
int cnt1,cnt2,top,sz[maxn],dep[maxn];
node st[maxn];
inline void merge(int u,int v){
int x=find(u),y=find(v);
if(dep[x]>dep[y])swap(x,y);
if(x==y)return ;
f[x]=y;sz[y]+=sz[x];if(dep[x]==dep[y])dep[y]++;
}
inline void merge2(int u,int v){
int x=find(u),y=find(v);
//printf("%d %d\n",x,y);
if(sz[x]>sz[y])swap(x,y);
if(x==y)return ;
st[++top]=node(x,f[x],y,sz[x]);
f[x]=y;sz[y]+=sz[x];if(dep[x]==dep[y])dep[y]++;
}
int g[maxn],ans[maxn],pos[maxn],vis[maxn];
void work(){
cnt1=cnt2=0;
for(int i=1;i<=n;i++){
f[i]=i;sz[i]=1;dep[i]=1;
}
for(int i=1;i<=m;i++){
g[i]=0;
}
for(int i=1;i<=cnt;i++){
if(q[i].op==1){alfa[++cnt1]=q[i];g[q[i].s]=1;}
else beta[++cnt2]=q[i];
}
for(int i=1;i<=m;i++)pos[e[i].id]=i;
sort(beta+1,beta+1+cnt2,cmp2);
int p=1;
for(int i=1;i<=cnt2;i++){
while(e[p].w>=beta[i].w&&p<=m){
if(g[e[p].id]){
p++;
}
else{
//printf("%d %d %d\n",e[p].u,e[p].v,e[p].w);
merge(e[p].u,e[p].v);p++;
}
}
int flag=1;
for(int j=1;j<=cnt1;j++){
if(alfa[j].id>beta[i].id){
for(int k=j-1;k>=1;k--){
if(vis[pos[alfa[k].s]])continue;
vis[pos[alfa[k].s]]=1;
if(alfa[k].w>=beta[i].w){
//printf("%d %d %d?\n",e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v,alfa[k].w);
merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
}
}
for(int k=j;k<=cnt1;k++){
if(vis[pos[alfa[k].s]])continue;
if(e[pos[alfa[k].s]].w>=beta[i].w){
//printf("%d %d %d.\n",e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v,e[pos[alfa[k].s]].w);
merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
}
}
for(int k=1;k<=cnt1;k++)vis[pos[alfa[k].s]]=0;
flag=0;
break;
}
}
if(flag){
for(int k=cnt1;k>=1;k--){
if(vis[pos[alfa[k].s]])continue;
vis[pos[alfa[k].s]]=1;
if(alfa[k].w>=beta[i].w){
merge2(e[pos[alfa[k].s]].u,e[pos[alfa[k].s]].v);
}
}
for(int k=1;k<=cnt1;k++)vis[pos[alfa[k].s]]=0;
}
ans[beta[i].id]=sz[find(beta[i].s)];
//printf("%d %d!\n",beta[i].s,ans[beta[i].id]);
while(top){
int u=st[top].u,val=st[top].v,v=st[top].w;int flag=st[top].id;
f[u]=val;sz[v]-=flag;sz[u]=flag;
top--;
}
}
for(int i=1;i<=cnt1;i++){
e[pos[alfa[i].s]].w=alfa[i].w;
}
int tot1=0,tot2=0,tot3=0;
for(int i=1;i<=m;i++){
if(g[e[i].id])e2[++tot2]=e[i];
else e1[++tot1]=e[i];
}
sort(e2+1,e2+1+tot2,cmp);
int p1=1,p2=1;
while(p1<=tot1&&p2<=tot2){
if(e1[p1].w>e2[p2].w){tot3++;e[tot3]=e1[p1];p1++;}
else {tot3++;e[tot3]=e2[p2];p2++;}
}
while(p1<=tot1){tot3++;e[tot3]=e1[p1],p1++;}
while(p2<=tot2){tot3++;e[tot3]=e2[p2],p2++;}
}
int main(){
//freopen("luogu5443.in","r",stdin);
//freopen("luogu5443.out","w",stdout);
n=read(),m=read();
int lim=sqrt(m*log(n));
//printf("%d\n",lim);
for(int i=1;i<=m;i++){
e[i].u=read(),e[i].v=read();e[i].w=read();e[i].id=i;
}
sort(e+1,e+1+m,cmp);
Q=read();
for(int i=1;i<=Q;i++){
q[++cnt].op=read();q[cnt].s=read(),q[cnt].w=read();q[cnt].id=i;
op[i]=q[cnt].op;
if(cnt==lim){work();cnt=0;}
}
if(cnt)work();
for(int i=1;i<=Q;i++){
if(op[i]==2){
printf("%d\n",ans[i]);
}
}
return 0;
}