文章目錄
- 寫在前面
- T1 [Grass Planting G](https://www.luogu.com.cn/problem/P3038)
- T2 [Max Flow P](https://www.luogu.com.cn/problem/P3128)
- T3 [求和](https://www.luogu.com.cn/problem/P4427)
- T4 [Cow Land G](https://www.luogu.com.cn/problem/P6098)
- T5 [Tree](https://www.luogu.com.cn/problem/P4092)
- T6 [Travel](https://www.luogu.com.cn/problem/P3976)
- T7 [月下“毛景樹”](https://www.luogu.com.cn/problem/P4315)
- T8 [Magical Tree](https://www.luogu.com.cn/problem/P3833)
- T9 [軟件包管理器](https://www.luogu.com.cn/problem/P2146)
寫在前面
樹剖錯了好多的地方:
- 跳鏈的時候注意什麼時候是 dep,什麼時候是 dfn
- DFS 的時候注意
v==fa||v==son
的時候不要隨手 return 掉 - 但是在 DFS2 的時候 u 沒有 son 要 return 掉
- 映射大法好!(前提是你實在整不出來了,那就狂加數組維護映射關係)
- 蒟蒻現在才搞清楚維護是什麼意思,至於樹剖的維護,想清楚維護什麼就行了
T1 Grass Planting G
顯然樹剖只能加作用於點權,於是想辦法把邊權轉化爲點權
考慮邊權化點權:
把邊權下放到兒子上去
於是區間修改,單點查詢
- 首先,讀圖,預DFS 求父親、深度、子樹大小、重兒子
- 然後,DFS 求 DFS序 、重鏈頂
- 最後,建 SGT,讀操作
- 對於路徑加邊權,區間修改,注意不要把 LCA 加了
方法:最後得到的中,若,易知爲 LCA,那麼不加就行了,即 - 對於路徑求邊權,單點查詢,查就是了
- 對於路徑加邊權,區間修改,注意不要把 LCA 加了
注意兩個函數:要寫對
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
int n,m,sz;
vector<int>G[NNN];
int fat[NNN];
int son[NNN];
int siz[NNN];
int top[NNN];
int dep[NNN];
int dfn[NNN],tot;
int sum[NNN<<2];
int laz[NNN<<2];
int wei[NNN<<2];
inline void push_up(int p){
sum[p]=sum[p<<1]+sum[p<<1|1];
}
inline void push_down(int p,int len){
laz[p<<1]+=laz[p];
laz[p<<1|1]+=laz[p];
sum[p<<1]+=laz[p]*(len-(len>>1));
sum[p<<1|1]+=laz[p]*(len>>1);
laz[p]=0;
}
inline void pre_DFS(int u,int fa){
fat[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
int heavy=0;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa)continue;
pre_DFS(v,u);
siz[u]+=siz[v];
if(heavy<siz[v])
heavy=siz[v],son[u]=v;
}
}
inline void DFS(int u,int pik){
dfn[u]=++tot;
top[u]=pik;
if(!son[u])return;
DFS(son[u],pik);
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v^son[u]&&v^fat[u])
DFS(v,v);
}
}
inline void build(int p,int l,int r){
if(l==r){
sum[l]=0;
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
return;
}
inline void update(int p,int l,int r,int L,int R){
if(L<=l&&r<=R){
sum[p]+=r-l+1;
laz[p]++;
return;
}
push_down(p,r-l+1);
int mid=(l+r)>>1;
if(L<=mid) update(p<<1,l,mid,L,R);
if(R>mid) update(p<<1|1,mid+1,r,L,R);
push_up(p);
return;
}
inline int query(int p,int l,int r,int k){
if(l==r) return sum[p];
push_down(p,r-l+1);
int mid=(l+r)>>1,res=0;
if(k<=mid) res+=query(p<<1,l,mid,k);
else res+=query(p<<1|1,mid+1,r,k);
return res;
}
inline void Modify(){
int u=in,v=in;
while(top[u]^top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,1,n,dfn[top[u]],dfn[u]);
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,1,n,dfn[u]+1,dfn[v]);
}
inline void Query(){
int u=in,v=in;
if(fat[v]==u) swap(u,v);
printf("%d\n",query(1,1,n,dfn[u]));
return;
}
int main(){
n=in,m=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
pre_DFS(1,0);
DFS(1,1);
build(1,1,n);
for(int i=1;i<=m;++i){
char opt=getchar();
while(!isalpha(opt)) opt=getchar();
if(opt=='P') Modify();
else Query();
}
return 0;
}
T2 Max Flow P
什麼鬼。。。最大流P
這兩天考試的網絡流已經夠了,而且我還不會
幸好這是數據結構
不說了,區間修改,求整體最大值
- 首先,讀圖,找重兒子,父親,深度,子樹
- 然後,giao 出 DFS序、重鏈頂
- 最後,建 SGT,giao 操作
區間修改,直接返回線段樹根節點的 val
寫樹狀數組寫炸了(其實可以寫)
一個下午,重構三遍,堵死我也
經驗:讀圖(條邊),跳鏈注意數組,注意邊界與區間開閉
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
int n,m;
int first[NNN],nxt[NNN<<1],aim[NNN],etot;
int fat[NNN],son[NNN],top[NNN],siz[NNN],dfn[NNN],dep[NNN],tot;
struct Tree{
int val,l,r,laz;
}tr[NNN<<2];
inline void add(int u,int v){
nxt[++etot]=first[u];
first[u]=etot;
aim[etot]=v;
return;
}
inline void DFS1(int fa,int u){
fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int pik){
top[u]=pik,dfn[u]=++tot;
if(!son[u]) return;
DFS2(son[u],pik);
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==son[u]||v==fat[u]) continue;
DFS2(v,v);
}
return;
}
inline void push_up(int p){
tr[p].val=max(tr[lch].val,tr[rch].val);
return;
}
inline void push_down(int p){
if(!tr[p].laz) return;
tr[lch].laz+=tr[p].laz;
tr[rch].laz+=tr[p].laz;
tr[lch].val+=tr[p].laz;
tr[rch].val+=tr[p].laz;
tr[p].laz=0;
return;
}
inline void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline void update(int p,int l,int r){
if(l<=tr[p].l&&tr[p].r<=r){
++tr[p].val;
++tr[p].laz;
return;
}
push_down(p);
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) update(lch,l,r);
if(r>mid) update(rch,l,r);
push_up(p);
return;
}
inline void Modify(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,dfn[top[u]],dfn[u]);
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,dfn[u],dfn[v]);
return;
}
signed main(){
n=in,m=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
add(u,v),add(v,u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=m;++i) Modify(in,in);
printf("%lld\n",tr[1].val);
return 0;
}
T3 求和
快速冪一波
inline int qpow(int a,int x){
int res=1;
while(x){
if(x&1) res=(res*a)%MOD;
a=(a*a)%MOD;
x>>=1;
}
return res%MOD;
}
現在是權值,仍線段樹最底層去,來單點改,區間和
但是這題樹剖常數大,要T掉一個點
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int MOD=998244353;
const int NNN=5e5+10;
int n,m;
vector<int>G[NNN];
int fat[NNN],siz[NNN],top[NNN],dep[NNN],dfn[NNN],son[NNN],tot;
int zOz[NNN];//dfn→dep
int tre[NNN<<2];//記錄原數據
int val[NNN<<2];//記錄冪
bool exi[NNN<<2];
inline int qpow(int a,int x){
int res=1;
while(x){
if(x&1) res=(res*a)%MOD;
a=(a*a)%MOD;
x>>=1;
}
return res%MOD;
}
inline void DFS1(int fa,int u){
fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int pik){
top[u]=pik,dfn[u]=++tot,zOz[tot]=dep[u];
if(!son[u]) return;
DFS2(son[u],pik);
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==son[u]||v==fat[u]) continue;
DFS2(v,v);
}
return;
}
inline void build(int p,int l,int r){
if(l==r){
tre[p]=zOz[l];
return;
}
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline int query(int p,int l,int r,int L,int R,int k){
if(l==r){
val[p]=qpow(tre[p],k)%MOD;
// exi[p]=true;printf("%d %d %d %d\n",p,exi[p],tre[p],val[p]);
return val[p];
}
int mid=(l+r)>>1,res=0;
if(L<=mid) res=(res+query(lch,l,mid,L,R,k))%MOD;
if(R>mid) res=(res+query(rch,mid+1,r,L,R,k))%MOD;
return res;
}
inline int sum(int u,int v,int k){
int res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=(res+query(1,1,n,dfn[top[u]],dfn[u],k))%MOD;
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res=(res+query(1,1,n,dfn[u],dfn[v],k))%MOD;
return res;
}
signed main(){
n=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
DFS1(0,1);
for(int i=1;i<=n;++i) --dep[i];
DFS2(1,1);
build(1,1,n);
m=in;
for(int i=1;i<=m;++i){
// memset(exi,0,sizeof(exi));
int u=in,v=in,k=in;
printf("%d\n",sum(u,v,k));
// for(int i=1;i<=n*4;++i) printf("%d %d %d %d\n",i,exi[i],tre[i],val[i]);puts("");
}
return 0;
}
也可以這樣優化一下(然鵝並不能過那個T掉的點)
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int MOD=998244353;
const int NNN=5e5+10;
int n,m;
vector<int>G[NNN];
int fat[NNN],siz[NNN],top[NNN],dep[NNN],dfn[NNN],son[NNN],tot;
int zOz[NNN];
int tre[NNN<<2][51];
inline int qpow(int a,int x){
int res=1;
while(x){
if(x&1) res=(res*a)%MOD;
a=(a*a)%MOD;
x>>=1;
}
return res%MOD;
}
inline void DFS1(int fa,int u){
fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int pik){
top[u]=pik,dfn[u]=++tot,zOz[tot]=dep[u];
if(!son[u]) return;
DFS2(son[u],pik);
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==son[u]||v==fat[u]) continue;
DFS2(v,v);
}
return;
}
inline void build(int p,int l,int r){
if(l==r){
tre[p][1]=zOz[l];
return;
}
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline int query(int p,int l,int r,int L,int R,int k){
if(l==r) return tre[p][k];
int mid=(l+r)>>1,res=0;
if(L<=mid) res=(res+query(lch,l,mid,L,R,k))%MOD;
if(R>mid) res=(res+query(rch,mid+1,r,L,R,k))%MOD;
return res;
}
inline int sum(int u,int v,int k){
int res=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
res=(res+query(1,1,n,dfn[top[u]],dfn[u],k))%MOD;
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
res=(res+query(1,1,n,dfn[u],dfn[v],k))%MOD;
return res;
}
signed main(){
n=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
DFS1(0,1);
for(int i=1;i<=n;++i) --dep[i];
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=(n<<2);++i)
if(tre[i][1])
for(int j=2;j<=50;++j)
tre[i][j]=qpow(tre[i][1],j);
m=in;
for(int i=1;i<=m;++i){
int u=in,v=in,k=in;
printf("%d\n",sum(u,v,k));
}
return 0;
}
本解法線段樹一定要搜到底,就不優,就很淦,就離譜
T4 Cow Land G
異或顯然有性質:
- 首先,來一波線段樹常規操作:讀圖,兩次 DFS,建樹
- 然後,對於查詢,查就完了;對於修改,要查到底
如果要考慮差分的話,碼主席樹吧
做兩道題了,發現了一個常見套路:
建樹的時候要求的值,其中是線段樹節點到價值的映射,而我們有的映射只有樹上節點編號到價值的映射,又發現是自帶線段樹節點到DFS序的映射的,於是考慮複合映射:
我好NaN啊,因此就用NaN代替了吧
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e6+10;
int n,q,e[NNN];
vector<int>G[NNN];
int fat[NNN],son[NNN],dep[NNN],top[NNN],siz[NNN],dfn[NNN],tot;
int tre[NNN<<2];
int NaN[NNN];//dfn->e
inline void DFS1(int fa,int u){
fat[u]=fa,siz[u]=1,dep[u]=dep[fa]+1;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int pik){
dfn[u]=++tot,top[u]=pik,NaN[tot]=e[u];
if(!son[u]) return;
DFS2(son[u],pik);
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fat[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
inline void push_up(int p){
tre[p]=tre[lch]^tre[rch];
return;
}
inline void build(int p,int l,int r){
if(l==r){
tre[p]=NaN[l];
return;
}
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
push_up(p);
return;
}
inline void update(int p,int l,int r,int k,int w){
if(l==r){
tre[p]=w;
return;
}
int mid=(l+r)>>1;
if(k<=mid) update(lch,l,mid,k,w);
else update(rch,mid+1,r,k,w);
push_up(p);
return;
}
inline int query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R) return tre[p];
int ans=0,mid=(l+r)>>1;
if(L<=mid) ans^=query(lch,l,mid,L,R);
if(R>mid) ans^=query(rch,mid+1,r,L,R);
return ans;
}
inline void Modify(){
int u=in,w=in;
update(1,1,n,dfn[u],w);
return;
}
inline void Have_dinner(){
int u=in,v=in,ans=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans^=query(1,1,n,dfn[top[u]],dfn[u]);
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans^=query(1,1,n,dfn[u],dfn[v]);
printf("%d\n",ans);
return;
}
signed main(){
n=in,q=in;
for(int i=1;i<=n;++i) e[i]=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=q;++i){
int opt=in;
if(opt==1) Modify();
else Have_dinner();//查詢函數,並且表示我餓了
}
return 0;
}
總結經驗:int變long,數組開大,AC你拿
T5 Tree
考慮打標記時整個子樹中有標記的點之間點所求祖先都是父親
發現 DFS序 就足夠解決了,但是不優,只能算個暴力
省了很多樹剖的內容,但是沒有刪完,按理說只用 DFS序 就夠了
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e6+10;
int n,q;
vector<int>g[NNN];
int fat[NNN],son[NNN],siz[NNN],dfn[NNN],tot;
int tre[NNN<<2],idx[NNN];
inline void DFS1(int fa,int u){
fat[u]=fa,siz[u]=1;
for(int e=0;e<g[u].size();++e){
int v=g[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int pik){
dfn[u]=++tot;
if(!son[u]) return;
DFS2(son[u],pik);
for(int e=0;e<g[u].size();++e){
int v=g[u][e];
if(v==fat[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
inline void build(int p,int l,int r){
if(l==r){
tre[p]=1;
idx[l]=p;
return;
}
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline void Mark(){
int u=in;
int l=dfn[u],r=dfn[u]+siz[u]-1;
for(int i=l;i<=r;++i){
tre[idx[i]]=max(tre[idx[i]],u);
}
return;
}
inline void Inquire(){
int u=in;
printf("%lld\n",tre[idx[dfn[u]]]);
return;
}
signed main(){
n=in,q=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
g[u].push_back(v);
g[v].push_back(u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=q;++i){
char opt=getchar();
while(opt!='C'&&opt!='Q') opt=getchar();
if(opt=='C') Mark();
else Inquire();
}
return 0;
}
正解:
考慮樹剖(別問我怎麼想到的,自己看看標題)
在跳重鏈的時候,如果經過了合法祖先,那麼得到答案
考慮 SGT 上維護 區間最深祖先 ,那麼搜索的時候搜到了該祖先就可以輸出了
至於其它點,SGT 上隨便填個 0 或 -1 就行了,不影響
#include<bits/stdc++.h>
#define in Read()
#define lch p<<1
#define rch p<<1|1
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
int n,q;
vector<int>G[NNN];
int fat[NNN];
int son[NNN];
int dep[NNN];
int siz[NNN];
int dfn[NNN];//u->dfn
int top[NNN];
int idx[NNN];//dfn->u
int tot;
int tre[NNN<<2];//SGT_p->u
inline void DFS1(int fa,int u){
fat[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int qwq){
dfn[u]=++tot;
idx[tot]=u;
top[u]=qwq;
if(!son[u]) return;
DFS2(son[u],qwq);
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fat[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
inline void push_up(int p){
if(dep[tre[p]]<dep[tre[lch]]) tre[p]=tre[lch];
if(dep[tre[p]]<dep[tre[rch]]) tre[p]=tre[rch];
return;
}
inline void build(int p,int l,int r){
if(l==r){
tre[p]=0;
return;
}
int mid=(l+r)>>1;
build(lch,l,mid);
build(rch,mid+1,r);
push_up(p);
return;
}
inline void update(int p,int l,int r,int k,int w){
if(l==r){
tre[p]=w;
return;
}
int mid=(l+r)>>1;
if(k<=mid) update(lch,l,mid,k,w);
else update(rch,mid+1,r,k,w);
push_up(p);
return;
}
inline int query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R) return tre[p];
int mid=(l+r)>>1,tmp,res=0;
if(L<=mid){
tmp=query(lch,l,mid,L,R);
if(dep[tmp]>dep[res]) res=tmp;
}
if(R>mid){
tmp=query(rch,mid+1,r,L,R);
if(dep[tmp]>dep[res]) res=tmp;
}
return res;
}
inline void buy_kvass(int u){
update(1,1,n,dfn[u],u);
return;
}
inline int kvass(int u){
int v=1,ans;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans=query(1,1,n,dfn[top[u]],dfn[u]);
if(ans) return ans;
u=fat[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans=query(1,1,n,dfn[u],dfn[v]);
return ans?ans:1;
}
inline void drink_kvass(int u){
printf("%d\n",kvass(u));
return;
}
int main(){
n=in,q=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
for(int i=1;i<=q;++i){
char opt=getchar();
while(opt!='Q'&&opt!='C') opt=getchar();
if(opt=='C') buy_kvass(in);//put a tag on the dot, or update the deepest dot
else drink_kvass(in);//get the deepest legal ancestor
}
return 0;
}
心情不好
變量名有點奇怪,抱歉
不過,
T6 Travel
考試題,考場上樹剖4K碼炸了,整出來了一個只能正確求出從深度大的點走到深度小的點的代碼
考慮維護幾個值:(樹剖你維護就是了,主要是要想清楚要維護什麼)
區間 max,區間 min,兩個方向的利潤最大值( forward , bakward ),當然還需要 lazy_tag
其中
變量名取長了就醜
調了一個晚上 😃
(不過機房人誰不是這樣的呢)
感謝 gigo 巨佬,Our best wishes are given him and we pray him to AK IOI
最後發現是我用了C++11,洛谷炸了,洛谷不能用C++11
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
const int INF=1e9+10;
int n,q;
int tot;
vector<int>G[NNN];
int wei[NNN];//u->value
int faz[NNN];
int son[NNN];
int dep[NNN];
int dfn[NNN];
int idx[NNN];//inverse map of dfn
int ord;//dfs order
int siz[NNN];
int top[NNN];
int sgn[NNN];//to mark whether to take forward or backward
struct SGT{
int l,r;
int max_;
int min_;
int lmax;//go from down to up, forward
int rmax;//go from up to down, backward
int lazy;
inline void Clear(){
max_=0;
min_=INF;
lmax=0;
rmax=0;
return;
}
}tre[NNN<<2];
inline void DFS1(int fa,int u){
faz[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
for(int e=0;e<G[u].size();++e){
int v=G[u][e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int qwq){
dfn[u]=++ord;
top[u]=qwq;
idx[ord]=u;
if(!son[u]) return;
DFS2(son[u],qwq);
for(int e=0;e<G[u].size();e++){
int v=G[u][e];
if(v==faz[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
#define lch p<<1
#define rch p<<1|1
#define tl tre[p].l
#define tr tre[p].r
inline void push_up(int p){
tre[p].max_=max(tre[lch].max_,tre[rch].max_);
tre[p].min_=min(tre[lch].min_,tre[rch].min_);
tre[p].lmax=max(tre[lch].max_-tre[rch].min_,max(tre[lch].lmax,tre[rch].lmax));
tre[p].rmax=max(tre[rch].max_-tre[lch].min_,max(tre[lch].rmax,tre[rch].rmax));
return;
}
inline void push_down(int p){
if(!tre[p].lazy) return;
tre[lch].max_+=tre[p].lazy;
tre[rch].max_+=tre[p].lazy;
tre[lch].min_+=tre[p].lazy;
tre[rch].min_+=tre[p].lazy;
tre[lch].lazy+=tre[p].lazy;
tre[rch].lazy+=tre[p].lazy;
tre[p].lazy=0;
return;
}
inline SGT merge(SGT l,SGT r){
SGT res;
res.max_=max(l.max_,r.max_);
res.min_=min(l.min_,r.min_);
res.lmax=max(l.max_-r.min_,max(l.lmax,r.lmax));
res.rmax=max(r.max_-l.min_,max(l.rmax,r.rmax));
return res;
}
inline void build(int p,int l,int r){
tl=l;tr=r;
if(l==r){
tre[p].max_=wei[idx[l]];
tre[p].min_=wei[idx[l]];
return;
}
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
push_up(p);
return;
}
inline SGT query(int p,int l,int r){
if(l<=tl&&tr<=r)
return tre[p];
push_down(p);
int mid=tl+tr>>1;
if(r<=mid) return query(lch,l,r);
if(l>mid) return query(rch,l,r);
return merge(query(lch,l,r),query(rch,l,r));
}
inline void update(int p,int l,int r,int w){
if(l<=tl&&tr<=r){
tre[p].max_+=w;
tre[p].min_+=w;
tre[p].lazy+=w;
return;
}
push_down(p);
int mid=tr+tl>>1;
if(l<=mid) update(lch,l,r,w);
if(r>mid) update(rch,l,r,w);
push_up(p);
return;
}
#undef lch
#undef rch
#undef tl
#undef tr
inline int Sum(int u,int v){
SGT l,r;
l.Clear();r.Clear();
l.min_=INF;
r.min_=INF;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
r=merge(query(1,dfn[top[v]],dfn[v]),r);
v=faz[top[v]];
}else{
l=merge(query(1,dfn[top[u]],dfn[u]),l);
u=faz[top[u]];
}
}
if(dep[u]>dep[v]) l=merge(query(1,dfn[v],dfn[u]),l);
else r=merge(query(1,dfn[u],dfn[v]),r);
swap(l.lmax,l.rmax);
return merge(l,r).rmax;
}
inline void Modify(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,dfn[top[u]],dfn[u],w);
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,dfn[u],dfn[v],w);
return;
}
signed main(){
// freopen("my.out","w",stdout);
n=in;
for(int i=1;i<=n;++i) wei[i]=in;
for(int i=2;i<=n;++i){
int u=in,v=in;
G[u].push_back(v);
G[v].push_back(u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
q=in;
for(int i=1;i<=q;++i){
int u=in,v=in,w=in;
printf("%lld\n",Sum(u,v));
Modify(u,v,w);
}
return 0;
}
T7 月下“毛景樹”
此題不板,何板之有?
是不板,孰板?
同 T1,需要邊權下放到點權,考慮 DFS1 的時候就下放
注意 LCA 不能算進去
gigo 大佬是算了 LCA ,調了一下午,我是沒考慮 cover 和 add 操作的優先級,調了一下午
cover 比 add 要優先一些,因爲 cover 了 add 就沒用了
單獨下放邊權,思路比較清晰一些(giaohr巨巨的代碼;並且我請求大家不要訪問他的博客,因爲這樣會增加他的訪問量)
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+5;
const int INF=1e9+7;
int n;
char q[50];
int tot;
int first[NNN];
int nxt[NNN<<1];
int aim[NNN<<1];
int wei[NNN];
int to1[NNN];
int to2[NNN];
int faz[NNN];
int dep[NNN];
int siz[NNN];
int son[NNN];
int top[NNN];
int dfn[NNN];
int idx[NNN];
int dwn[NNN];
int val[NNN];
int ord;
struct Tree{
int l,r;
int max;
int add;
int cov;
}tre[NNN<<2];
inline void add(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
inline void DFS1(int fa,int u){
faz[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int tp){
top[u]=tp;
dfn[u]=++ord;
idx[ord]=u;
if(!son[u]) return;
DFS2(son[u],tp);
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==faz[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
inline void push_road(){
for(int i=1;i<n;++i){
dwn[i]=dep[to1[i]]<dep[to2[i]]?to2[i]:to1[i];
val[dwn[i]]=wei[i];
}
return;
}
#define lch p<<1
#define rch p<<1|1
#define tr tre[p].r
#define tl tre[p].l
inline void push_up(int p){
tre[p].max=max(tre[lch].max,tre[rch].max);
return ;
}
inline void push_down(int p){
if(tre[p].cov!=-1){
tre[lch].max=tre[p].cov;
tre[rch].max=tre[p].cov;
tre[lch].cov=tre[p].cov;
tre[rch].cov=tre[p].cov;
tre[lch].add=0;
tre[rch].add=0;
tre[p].cov=-1;
}
if(tre[p].add){
tre[lch].add+=tre[p].add;
tre[rch].add+=tre[p].add;
tre[lch].max+=tre[p].add;
tre[rch].max+=tre[p].add;
tre[p].add=0;
}
return;
}
inline void build(int p,int l,int r){
tr=r,tl=l;
tre[p].cov=-1;
tre[p].max=-INF;
if(l==r){
tre[p].max=val[idx[l]];
return;
}
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
push_up(p);
return;
}
inline void cover(int p,int l,int r,int w){
if(l<=tl&&tr<=r){
tre[p].max=w;
tre[p].cov=w;
tre[p].add=0;
return;
}
push_down(p);
int mid=tl+tr>>1;
if(l<=mid) cover(lch,l,r,w);
if(r>mid) cover(rch,l,r,w);
push_up(p);
return;
}
inline void update(int p,int l,int r,int w){
if(l<=tl&&tr<=r){
tre[p].max+=w;
tre[p].add+=w;
return;
}
push_down(p);
int mid=tr+tl>>1;
if(l<=mid) update(lch,l,r,w);
if(r>mid) update(rch,l,r,w);
push_up(p);
return;
}
inline int query(int p,int l,int r){
if(l<=tl&&tr<=r) return tre[p].max;
int ans=0;
push_down(p);
int mid=tr+tl>>1;
if(l<=mid) ans=max(ans,query(lch,l,r));
if(r>mid) ans=max(ans,query(rch,l,r));
return ans;
}
#undef lch
#undef rch
#undef tr
#undef tl
inline void Cover(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
cover(1,dfn[top[u]],dfn[u],w);
u=faz[top[u]];
}
if(u==v) return;
if(dep[u]>dep[v]) swap(u,v);
cover(1,dfn[u]+1,dfn[v],w);
return;
}
inline void Add(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,dfn[top[u]],dfn[u],w);
u=faz[top[u]];
}
if(u==v) return;
if(dep[u]>dep[v]) swap(u,v);
update(1,dfn[u]+1,dfn[v],w);
return;
}
inline int Query(int u,int v){
int ans=-INF;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans=max(ans,query(1,dfn[top[u]],dfn[u]));
u=faz[top[u]];
}
if(u==v) return ans;
if(dep[u]>dep[v]) swap(u,v);
ans=max(ans,query(1,dfn[u]+1,dfn[v]));
return ans;
}
signed main(){
// freopen("moon.in","r",stdin);
// freopen("moon.out","w",stdout);
n=in;
for(int i=1;i<n;++i){
int u=in,v=in,w=in;
add(u,v);
add(v,u);
to1[i]=u;
to2[i]=v;
wei[i]=w;
}
DFS1(0,1);
DFS2(1,1);
push_road();
build(1,1,n);
scanf("%s",q);
while(q[0]!='S'){
if(q[1]=='h'){
int k=in,w=in;
cover(1,dfn[dwn[k]],dfn[dwn[k]],w);
}else if(q[1]=='o'){
int u=in,v=in,w=in;
Cover(u,v,w);
}else if(q[1]=='d'){
int u=in,v=in,w=in;
Add(u,v,w);
}else if(q[1]=='a'){
int u=in,v=in;
printf("%lld\n",Query(u,v));
}scanf("%s",q);
}
return 0;
}
另外的DFS1就下放的代碼,我先咕了~
T8 Magical Tree
這題有毒,週末想做,每次看到 Harry 和 鄧教 就毅然打開 B站 開始 三刷
然後一個週末就這樣荒廢了
不過我們終於迎來了一道 板子題
從 0 開始的點直接搞成從 1 開始,常規操作
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
int n,q;
int first[NNN];
int nxt[NNN<<1];
int aim[NNN<<1];
int tot;
int faz[NNN];
int son[NNN];
int top[NNN];
int dep[NNN];
int dfn[NNN];
int idx[NNN];
int ord;
int siz[NNN];
struct Tree{
int l,r;
int sum;
int laz;
}tre[NNN<<2];
inline void add(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
}
inline void DFS1(int fa,int u){
faz[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int tp){
top[u]=tp;
dfn[u]=++ord;
idx[ord]=u;
if(!son[u]) return;
DFS2(son[u],tp);
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==son[u]||v==faz[u]) continue;
DFS2(v,v);
}
return;
}
#define lch p<<1
#define rch p<<1|1
#define tl tre[p].l
#define tr tre[p].r
inline void push_up(int p){
tre[p].sum=tre[lch].sum+tre[rch].sum;
return;
}
inline void push_down(int p,int len){
if(!tre[p].laz) return;
tre[lch].sum+=tre[p].laz*(len-(len>>1));
tre[rch].sum+=tre[p].laz*(len>>1);
tre[lch].laz+=tre[p].laz;
tre[rch].laz+=tre[p].laz;
tre[p].laz=0;
return;
}
inline void build(int p,int l,int r){
tl=l;
tr=r;
if(l==r) return;
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline void update(int p,int l,int r,int w){
if(l<=tl&&tr<=r){
tre[p].sum+=w*(tr-tl+1);
tre[p].laz+=w;
return;
}
push_down(p,tr-tl+1);
int mid=tr+tl>>1;
if(l<=mid) update(lch,l,r,w);
if(r>mid) update(rch,l,r,w);
push_up(p);
return;
}
inline int query(int p,int l,int r){
if(l<=tl&&tr<=r) return tre[p].sum;
push_down(p,tr-tl+1);
int mid=tl+tr>>1,res=0;
if(l<=mid) res+=query(lch,l,r);
if(r>mid) res+=query(rch,l,r);
return res;
}
#undef lch
#undef rch
#undef tl
#undef tr
inline void Modify(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,dfn[top[u]],dfn[u],w);
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,dfn[u],dfn[v],w);
return;
}
signed main(){
n=in;
for(int i=1;i<n;++i){
int u=in,v=in;
++u,++v;
add(u,v);
add(v,u);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
q=in;
for(int i=1;i<=n;++i){
char s=getchar();
while(s!='A'&&s!='Q') s=getchar();
if(s=='A'){
int u=in,v=in,w=in;
++u,++v;
Modify(u,v,w);
}else if(s=='Q'){
int u=in;
++u;
printf("%lld\n",query(1,dfn[u],dfn[u]+siz[u]-1));
}
}
return 0;
}
T9 軟件包管理器
第一眼:拓撲排序?
發現是看走眼了
install
節點到根搜一遍爲 0 的,然後全部加上 1
uninstall
子樹根到子樹所有節點搜一遍爲 1 的,然後全部變爲 0
注意從某個點到頂點必須要樹剖一下,因爲跨越重鏈的 DFS序 不連續
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,q;
char s[50];
int first[N];
int nxt[N<<1];
int aim[N<<1];
int faz[N];
int son[N];
int dep[N];
int top[N];
int dfn[N];
int siz[N];
int tot;
int ord;
struct Tree{
int l,r;
int sum;
int laz;
}tre[N<<2];
inline void add(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
inline void DFS1(int fa,int u){
faz[u]=fa;
dep[u]=dep[fa]+1;
siz[u]=1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS1(u,v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
return;
}
inline void DFS2(int u,int tp){
top[u]=tp;
dfn[u]=++ord;
if(!son[u]) return;
DFS2(son[u],tp);
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==faz[u]||v==son[u]) continue;
DFS2(v,v);
}
return;
}
#define lch p<<1
#define rch p<<1|1
#define tr tre[p].r
#define tl tre[p].l
inline void push_up(int p){
tre[p].sum=tre[lch].sum+tre[rch].sum;
return;
}
inline void push_down(int p){
if(tre[p].laz==-1) return;
if(tre[p].laz==1){
tre[lch].laz=tre[p].laz;
tre[rch].laz=tre[p].laz;
tre[lch].sum=tre[lch].r-tre[lch].l+1;
tre[rch].sum=tre[rch].r-tre[rch].l+1;
tre[p].laz=-1;
}
if(tre[p].laz==0){
tre[lch].laz=tre[p].laz;
tre[rch].laz=tre[p].laz;
tre[lch].sum=0;
tre[rch].sum=0;
tre[p].laz=-1;
}
return;
}
inline void build(int p,int l,int r){
tl=l,tr=r;
tre[p].sum=0;
tre[p].laz=-1;
if(l==r) return;
int mid=l+r>>1;
build(lch,l,mid);
build(rch,mid+1,r);
return;
}
inline void update(int p,int l,int r,int w){
if(l<=tl&&tr<=r){
tre[p].sum=(tr-tl+1)*w;
tre[p].laz=w;
return;
}
push_down(p);
int mid=tr+tl>>1;
if(l<=mid) update(lch,l,r,w);
if(r>mid) update(rch,l,r,w);
push_up(p);
return;
}
inline int query(int p,int l,int r){
if(l<=tl&&tr<=r) return tre[p].sum;
int mid=tr+tl>>1,res=0;
push_down(p);
if(l<=mid) res+=query(lch,l,r);
if(r>mid) res+=query(rch,l,r);
push_up(p);
return res;
}
#undef lch
#undef rch
#undef tr
#undef tl
inline void Modify(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
update(1,dfn[top[u]],dfn[u],w);
u=faz[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
update(1,dfn[u],dfn[v],w);
return;
}
int main(){
n=in;
for(int i=1;i<n;++i){
int u=in;
add(u+1,i+1);
add(i+1,u+1);
}
DFS1(0,1);
DFS2(1,1);
build(1,1,n);
q=in;
for(int i=1;i<=q;++i){
scanf("%s",s);
int t1=tre[1].sum;
if(s[0]=='i'){
int x=in+1;
Modify(1,x,1);
printf("%d\n",abs(t1-tre[1].sum));
}else if(s[0]=='u'){
int x=in+1;
update(1,dfn[x],dfn[x]+siz[x]-1,0);
printf("%d\n",abs(t1-tre[1].sum));
}
}
return 0;
}
樹剖太NaN了,我剛多項式去了,暑假再回來