拿出切这道题的觉悟来!准备好草稿纸分类讨论环上情况(我没搭图床)
按照代码执行顺序看吧,我啃的地方都写出来了.
代码承袭自\(uoj\)的\(StormySea\)大佬
//这道题环的遍历顺序尤为重要
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char buf[1<<23],*p1=buf,*p2=buf;
#define getChar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<22,stdin),p1==p2)?EOF:*p1++)
const int N=600600;
inline int read(){
int x=0,f=1;
char ch=getChar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getChar();
}
while('0'<=ch&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getChar();
return x*f;
}
int n,m,q;
int dfn[N],low[N],Index,fa[N][22],tot,dep[N],Len[N],Rank[N];//用rank会CE
vector<int>mp[N],edge[N];
int pool[N<<2],*curpos=pool;//不空间池处理会mle
struct Bit{//树状数组(修改操作)
int size;
int *c;
void New(int x){
c=curpos;
size=x;
curpos+=size;
}
#define lowbit(i) i&(-i)
void add(int x,int val){
for(int i=x;i<=size;i+=lowbit(i))c[i]+=val;
}
int query(int x){
int ret=0;
for(int i=x;i;i-=lowbit(i))ret+=c[i];
return ret;
}
void update(int l,int r,int val){
add(l,val);
add(r+1,-val);//边权差分后区间修改
}
}f,g,h,ring[N];
//f表示root到i最短路和最长路的并集
//g表示root到i的最短路(新司机)
//h表示root到i的最长路(老司机)
//ring维护环上从顶端到底端的前缀和
//一条边的贡献:最长&最短,最长,最短,未经过,上述结构体可以导出这些值
void tarjan(int u){
dfn[u]=low[u]=++Index;
int v;
for(int i=0;i<edge[u].size();++i){
v=edge[u][i];
if(v==fa[u][0])continue;
if(!dfn[v]){
fa[v][0]=u;
dep[v]=dep[u]+1;//仙人掌上dfs树深度
tarjan(v);
low[u]=min(low[u],low[v]);
if(dfn[u]<low[v]){
mp[u].push_back(v);
}
}
else low[u]=min(low[u],dfn[v]);
}
for(int i=0;i<edge[u].size();++i){
v=edge[u][i];
if(fa[v][0]!=u&&dfn[v]>dfn[u]){
++tot;
mp[u].push_back(tot);
Rank[u]=0;
Len[tot]=dep[v]-dep[u]+1;//环长
// Len[tot]和u对应的都是环顶点,便于环上边权和处理
for(int j=v;j!=u;j=fa[j][0]){
Rank[j]=dep[j]-dep[u];//给环上点排名
mp[tot].push_back(j);
}
reverse(mp[tot].begin(),mp[tot].end());//按排名从小到大遍历环
ring[tot].New(Len[tot]+3);//开空间,tot加大了可以被卡
}
}
}
int l[N],r[N],num;//dfs序下子树区间
void dfs(int u,int Fa){
fa[u][0]=Fa;
l[u]=++num;//l[u]表示u这个点的dfs序,环上处理作环顶点时不受本环上边权影响
for(int v,i=0;i<mp[u].size();++i){
v=mp[u][i];
dep[v]=dep[u]+1;//注意这里dep重新定义为圆方树上深度
dfs(v,u);
}
r[u]=num;
}
inline int getlca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;--i){
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(dep[x]==dep[y])break;
}
if(x==y)return x;
for(int i=19;i>=0;--i)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
inline void update(int u,int v,int w){
if(dep[u]<dep[v])swap(u,v);//先让u的深度大
if(dep[u]==dep[v]+1){//树边
f.update(l[u],r[u],w);
g.update(l[u],r[u],w);
h.update(l[u],r[u],w);
}
else if(dep[u]==dep[v]){//u,v不是环顶点,(u,v)是环上边
int sq=fa[u][0];//方点 square
int mid=mp[sq][Len[sq]>>1];//环上最中间的一条边Rank大的那个点
if(Rank[u]>Rank[v])
swap(u,v);//钦定u是边上Rank小的点
f.update(l[sq]+1,r[sq],w);//环顶点到环上圆点有两条路,所以所有点f+w
ring[sq].add(Rank[v],w);//环上前缀和
if((Rank[u]<<1)<Len[sq]){
h.update(l[sq]+1,r[u],w);//环顶点到u的点的最长路
g.update(l[v],l[mid]-1,w);//v到mid以前的点的最短路
h.update(l[mid],r[sq],w);//mid到环末端的点的最长路
}
else{
h.update(l[sq]+1,l[mid]-1,w);//环顶点到mid以前的点的最长路
g.update(l[mid],r[u],w);//mid到u的点的最短路
h.update(l[v],r[sq],w);//v到环末端的点的最长路
}
}
else{//v是环上顶点,u是环上紧挨v的点
int sq=fa[u][0];//园方树上:fa[sq]=v
int mid=mp[sq][Len[sq]>>1],flag=(Rank[u]==1);
f.update(l[sq]+1,r[sq],w);
ring[sq].add(flag?1:Len[sq],w);//更新从第一个点或从最后一个点
//发现两种情况可以合并
(flag?g:h).update(l[sq]+1,l[mid]-1,w);
(flag?h:g).update(l[mid],r[sq],w);//和上面类似,画图吧
}
}//考虑分析一条边被哪些情况包括
int U[N],V[N],W[N];
int k,size,x[N],y[N],type[N],a[N<<1];//最坏开双倍点
int sum[N][2];//处理树上差分
int px[N],py[N];//px,py记录一个方点上待询问的圆点
vector<int>Q[N];
inline void add_tag(int id,int x,int y,int t){
++sum[x][t],++sum[y][t];
a[++size]=x,a[++size]=y;
if(dep[x]<dep[y])swap(x,y);
int lca;
for(int i=19;i>=0;--i) {
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(dep[x]==dep[y])break;
}
if(x==y)lca=x;
else{
for(int i=19;i>=0;--i)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
lca=fa[x][0];
}
if(lca<=n){
px[id]=py[id]=lca;
}
else{
px[id]=x,py[id]=y;
a[++size]=x,a[++size]=y;
Q[lca].push_back(id);
}
--sum[px[id]][t],--sum[py[id]][t];
//如果是lca是方点,lca处的点双留着后面讨论
}
inline bool cmp(int aa,int bb){
return l[aa]<l[bb];
}
int s[N],top;
inline int getson(int x,int ed){
for(int i=19;i>=0;--i){
if(dep[fa[x][i]]>dep[ed])x=fa[x][i];
if(dep[x]==dep[ed]+1)break;
}
return x;
}
int fir[N],cnt,sz[N];//sz记录方点子树个数,包含了上传到i祖先和就在i所在环处理的两种情况
struct node{
int nxt,to;
}e[N<<1];
void add(int u,int v){
e[++cnt].nxt=fir[u];fir[u]=cnt;e[cnt].to=v;
}
inline int solve_circle(int u){
int ret=0;
for(int v,i=fir[u];i;i=e[i].nxt){
v=e[i].to;
for(int t=0;t<2;++t)sum[u][t]+=sum[v][t];
if(sum[v][0]&&sum[v][1]){
ret+=f.query(l[v])-f.query(l[u]);//查询(u,v)的路径
// dis(v,u)=dis(root,v)-dis(root,u)
}
else if(sum[v][0]){
ret+=g.query(l[v])-g.query(l[u]);
}
else if(sum[v][1]){
ret+=h.query(l[v])-h.query(l[u]);
}
}
return ret;
}
int S[N],Num,p[N];//s统计环上差分
inline int solve_square(int u){
int Cnt=sz[u];
Num=0;
for(int i=0;i<=Cnt;++i)S[i]=0;
for(int v,i=fir[u];i;i=e[i].nxt){//从Rank大的访问起
v=e[i].to;
for(int t=0;t<2;++t)sum[u][t]+=sum[v][t];//标记上传
if(sum[v][0]&&sum[v][1])++S[0];
else if(sum[v][0]){
(Rank[v]<<1)>Len[u]?++S[Cnt-Num]:(++S[0],--S[Cnt-Num]);
//考虑环上走哪边到u
//因为v的Rank单降,前面有的点对应边可通过差分判断选不选,所以这里Cnt-Num(对应环上准确位置)
}
else if(sum[v][1]){
(Rank[v]<<1)<Len[u]?++S[Cnt-Num]:(++S[0],--S[Cnt-Num]);
}
p[++Num]=Rank[v];
}
reverse(p+1,p+Cnt+1);//Num==Cnt
p[Cnt+1]=Len[u];
//1、要上传到u祖先的标记
for(int i=0;i<Q[u].size();++i){
int x=px[Q[u][i]],y=py[Q[u][i]],l,r;
if(Rank[x]>Rank[y])swap(x,y);//钦定x的Rank更小
l=lower_bound(p+1,p+Cnt+1,Rank[x])-p;
r=lower_bound(p+1,p+Cnt+1,Rank[y])-p;
if(type[Q[u][i]]!=(((Rank[y]-Rank[x])<<1)<Len[u])){
++S[l],--S[r];//在[l,r]处理
}
else{
++S[0],--S[l],++S[r];//在环顶端两边处理
}
//0 1分类讨论环上最短/长路走法
}//2、只在这个环处理
Q[u].clear();//即时清空
int ret=0;
for(int i=0;i<=Cnt;++i){
S[i+1]+=S[i];
if(S[i]){
ret+=ring[u].query(p[i+1])-ring[u].query(p[i]);//这一条链要选
}
}
return ret;
}
inline int query(){
if(k==0)return 0;
size=top=0;
for(int i=1;i<=k;++i){
add_tag(i,x[i],y[i],type[i]);
}
sort(a+1,a+size+1,cmp);
size=unique(a+1,a+size+1)-a-1;
int tmp=size;
for(int i=1;i<tmp;++i){
a[++size]=getlca(a[i],a[i+1]);
}
sort(a+1,a+size+1,cmp);
size=unique(a+1,a+size+1)-a-1;
s[++top]=a[1];
tmp=size;
for(int i=2;i<=tmp;++i){
int u=a[i],v;
while(top&&l[u]>r[s[top]])--top;
v=s[top];
if(v>n&&dep[v]+1<dep[u]){
int w=getson(u,v);
add(v,w),add(w,u);
s[++top]=a[++size]=w;
++sz[v],++sz[w];
}
else ++sz[v],add(v,u);
s[++top]=u;
//改成邻接表建图,每个环会先访问Rank大的点(自己分析)
}
inplace_merge(a+1,a+tmp+1,a+size+1,cmp);//新加入点排序方式相同(dfs序升序)
size=unique(a+1,a+size+1)-a-1;
//虚圆方树建立,避免方方边
int ret=0;
for(int i=size;i>=1;--i){//从后往前遍历完
if(a[i]<=n){
ret+=solve_circle(a[i]);//圆点
}
else ret+=solve_square(a[i]);//方点
}
//clear
cnt=0;
for(int i=1;i<=size;++i){
int v=a[i];
sz[v]=sum[v][0]=sum[v][1]=fir[v]=0;
}
return ret;
}
int main(){
// freopen("ex_train3.in","r",stdin);
n=read(),m=read(),q=read();
tot=n;
int u,v,w;
for(int i=1;i<=m;++i){
u=read(),v=read(),w=read();
edge[u].push_back(v),edge[v].push_back(u);
U[i]=u,V[i]=v,W[i]=w;
}
tarjan(1);
dfs(1,0);
for(int i=1;i<=19;++i){
for(int j=1;j<=tot;++j)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
f.New(tot+3),g.New(tot+3),h.New(tot+3);//开空间
for(int i=1;i<=m;++i){
update(U[i],V[i],W[i]);
}
while(q--){
k=read();
for(int i=1;i<=k;++i){
x[i]=read(),y[i]=read(),type[i]=read();
}
printf("%d\n",query());
int id=read(),w=read();
if(id==0)continue;
update(U[id],V[id],w-W[id]);
W[id]=w;//边权差分处理
}
return 0;
}
希望不会码风劝退...