bzoj3052: [wc2013]糖果公园
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 #define maxn 100005 8 typedef long long ll; 9 ll ans,Ans[maxn]; 10 int n,m,q,temp,cnt1,deep[maxn],b[maxn],sum[maxn],block[maxn],Log[maxn],bz[maxn][21],a[maxn],dfn[maxn],tot,now[maxn],prep[maxn<<1],son[maxn<<1],wi[maxn],vi[maxn]; 11 bool can[maxn]; 12 struct Tsegment{ 13 int l,r,id; 14 }qs[maxn]; 15 struct Fsegment{ 16 int u,v,bo,last; 17 }ch[maxn]; 18 void read(int &x){ 19 x=0; int f=1; char Ch; 20 for (Ch=getchar();Ch<'0'||Ch>'9';Ch=getchar()) if (Ch=='-') f=-1; 21 for (;Ch>='0'&&Ch<='9';Ch=getchar()) x=x*10+Ch-'0'; x*=f; 22 } 23 bool comp(Tsegment x,Tsegment y){ 24 if (block[dfn[x.l]]!=block[dfn[y.l]]) return block[dfn[x.l]]<block[dfn[y.l]]; 25 if (block[dfn[x.r]]!=block[dfn[y.r]]) return block[dfn[x.r]]<block[dfn[y.r]]; 26 return x.id<y.id; 27 } 28 void add(int x,int y){ 29 prep[++tot]=now[x],now[x]=tot,son[tot]=y; 30 } 31 void dfs(int x,int fa){ 32 dfn[x]=++temp,bz[x][0]=fa,deep[x]=deep[fa]+1; 33 for (int i=now[x],so=son[i];i;i=prep[i],so=son[i]){ 34 if (so==fa) continue; 35 dfs(so,x); 36 } 37 } 38 int Lca(int x,int y){ 39 if (x==y) return x; 40 if (deep[x]<deep[y]) swap(x,y); 41 while (deep[x]>deep[y]) x=bz[x][Log[deep[x]-deep[y]]]; 42 if (x==y) return x; 43 while (bz[x][0]!=bz[y][0]){ 44 for (int i=20;i>=0;i--) 45 if (bz[x][i]!=bz[y][i]){ 46 x=bz[x][i],y=bz[y][i]; 47 break; 48 } 49 } 50 return bz[x][0]; 51 } 52 void prepare(){ 53 temp=sqrt(n); 54 for (int i=1;i<=20;i++){ 55 for (int j=1;j<=n;j++){ 56 bz[j][i]=bz[bz[j][i-1]][i-1]; 57 } 58 } 59 for (int i=1;i<=n;i++) block[i]=(i-1)/temp+1; 60 } 61 void modify(int x){ 62 if (can[x]==0){ 63 can[x]=1; 64 sum[a[x]]++; 65 ans+=(1LL*wi[sum[a[x]]]*vi[a[x]]); 66 }else{ 67 can[x]=0; 68 ans-=(1LL*wi[sum[a[x]]]*vi[a[x]]); 69 sum[a[x]]--; 70 } 71 } 72 void change(int x,int y){ 73 for (;x!=y;){ 74 if (deep[x]<deep[y]) swap(x,y); 75 modify(x); x=bz[x][0]; 76 } 77 } 78 void work(){ 79 memset(sum,0,sizeof(sum)); 80 memset(can,0,sizeof(can)); 81 temp=0,ans=qs[0].l=qs[0].r=0; 82 for (int u,v,w,x,y,i=1;i<=cnt1;i++){ 83 u=qs[i].id; 84 if (temp<=u){ 85 for (int j=temp;j<=u;j++){ 86 if (ch[j].bo==0) continue; 87 x=ch[j].u,y=ch[j].v; 88 if (can[x]==1) modify(x),a[x]=y,modify(x); 89 else a[x]=y; 90 } 91 }else{ 92 for (int j=temp;j>=u;j--){ 93 if (ch[j].bo==0) continue; 94 x=ch[j].u,y=ch[j].v; 95 if (can[x]==1) modify(x),a[x]=ch[j].last,modify(x); 96 else a[x]=ch[j].last; 97 } 98 } temp=u; 99 int lca=Lca(qs[i].l,qs[i].r); 100 change(qs[i-1].l,qs[i].l); 101 change(qs[i-1].r,qs[i].r); 102 modify(lca); 103 Ans[qs[i].id]=ans; 104 modify(lca); 105 } 106 } 107 int main(){ 108 read(n),read(m),read(q); 109 for (int i=1;i<=n;i++) Log[i]=log2(i); 110 for (int i=1;i<=m;i++) read(vi[i]); 111 for (int i=1;i<=n;i++) read(wi[i]); 112 tot=0,memset(now,0,sizeof(now)); 113 for (int i=1,u,v;i<n;i++){ 114 read(u),read(v); 115 add(u,v),add(v,u); 116 } 117 for (int i=1;i<=n;i++) read(a[i]); 118 memset(deep,0,sizeof(deep)); 119 temp=cnt1=0,dfs(1,0); 120 prepare(); 121 for (int i=1;i<=q;i++) ch[i].bo=0; 122 for (int type,x,y,i=1;i<=q;i++){ 123 read(type),read(x),read(y); 124 if (type==1){ 125 qs[++cnt1].id=i,qs[cnt1].l=x,qs[cnt1].r=y; 126 if (dfn[x]>dfn[y]) swap(qs[cnt1].l,qs[cnt1].r); 127 }else{ 128 ch[i].u=x,ch[i].v=y,ch[i].bo=1; 129 } 130 } 131 sort(qs+1,qs+cnt1+1,comp); 132 for (int i=1;i<=n;i++) b[i]=a[i]; 133 for (int i=1;i<=q;i++){ 134 if (ch[i].bo==0) continue; 135 ch[i].last=b[ch[i].u],b[ch[i].u]=ch[i].v; 136 } 137 work(); 138 for (int i=1;i<=q;i++){ 139 if (ch[i].bo==1) continue; 140 printf("%lld\n",Ans[i]); 141 } 142 return 0; 143 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3052
题目大意:
做法:带修改的树上莫队算法。
我们对所有询问排序,怎么排序呢?按左端点所在块为第一关键字,按右端点所在块为第二关键字,按操作的序号(以下称为时间)为第三关键字排序,然后依次暴力转移即可,每次转移时,先将时间暴力转移,即:若上次做完时间停在2,这次操作的时间戳为5,我们则需要把3~4的修改操作执行,暴力即可,如果上次为5,这次为2,则需要倒着还原修改操作。时间转移后利用对称差转移位置即可。及时更新答案即可,记得用1LL乘一下且用Long Long。
带修改的树上莫队算法。