海亮寄 7.18
前言
业精于勤荒于嬉,行成于思毁于随
正文(加餐)
树论选讲,这是要 \(DAY^{-1}\) 的节奏
浅贴一个课件
关键词:大杂烩、最优化、树上数据结构
再浅贴一下题单
(好累啊,不想贴代码)
此博客水分含量极高,建议混合海亮食堂的饭菜食用
T1
树上背包板子题
(站在 oier 巨人的肩膀上,被 oi 暴打)
时间复杂度 \(O(n \times \min(n,m))\)
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=305;
int n,m,head[N],tot,f[N][N];
struct Edge{int to,nxt;}e[N<<1];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void dfs(int u,int fa){
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
for(int j=m+1;j>=1;j--)
for(int k=0;k<j;k++)
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
}
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
memset(head,-1,sizeof(head));tot=1;
cin>>n>>m;
for(int i=1;i<=n;i++){
int x;cin>>x>>f[i][1];
add(i,x),add(x,i);
}
dfs(0,-1);
cout<<f[0][m+1]<<'\n';
return 0;
}
T2
我曾经跨过山和大海,却还有比我命长的转移方程在等待
记 \(f_{u,i,0/1,0/1}\) 表示 \(u\) 子树内消耗了 \(i\) 个监控,自己 不放 / 放 监控,和 不存在 / 存在 儿子放监控的方案数
转移不码了,有脚就行
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,K=105,MOD=1e9+7;
int n,k,head[N],tot;
struct Edge{int to,nxt;}e[N<<1];
int f[N][K][2][2],g[K][2][2],siz[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void ADD(int &x,int y){
x%=MOD,y%=MOD;x=(x*1ll+y*1ll)%MOD;
return;
}
inline void dfs(int u,int fa){
f[u][0][0][0]=f[u][1][1][0]=1ll;
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
for(int j=0;j<=min(siz[u],k);j++){
g[j][0][0]=f[u][j][0][0];f[u][j][0][0]=0ll;
g[j][0][1]=f[u][j][0][1];f[u][j][0][1]=0ll;
g[j][1][0]=f[u][j][1][0];f[u][j][1][0]=0ll;
g[j][1][1]=f[u][j][1][1];f[u][j][1][1]=0ll;
}
for(int x=0;x<=min(siz[u],k);x++){
for(int y=0;y<=min(siz[v],k-x);y++){
// TMD 这转移方程比我命都长,比 lmy 还 lmy
ADD(f[u][x+y][0][0],1ll*g[x][0][0]*f[v][y][0][1]%MOD);
ADD(f[u][x+y][0][1],1ll*g[x][0][0]*f[v][y][1][1]%MOD+1ll*g[x][0][1]*((1ll*f[v][y][0][1]+1ll*f[v][y][1][1])%MOD)%MOD);
ADD(f[u][x+y][1][0],1ll*g[x][1][0]*((1ll*f[v][y][0][0]+1ll*f[v][y][0][1])%MOD)%MOD);
ADD(f[u][x+y][1][1],1ll*g[x][1][0]*((1ll*f[v][y][1][0]+1ll*f[v][y][1][1])%MOD)%MOD);
ADD(f[u][x+y][1][1],1ll*g[x][1][1]*((1ll*f[v][y][0][0]+1ll*f[v][y][0][1]+1ll*f[v][y][1][0]+1ll*f[v][y][1][1])%MOD)%MOD);
}
}
siz[u]+=siz[v];
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,0);
cout<<(f[1][k][0][1]+f[1][k][1][1])%MOD<<'\n';
return 0;
}
T3
神题(被墨鱼拯救的第一题)
转化一下题意,等价于在树上选出一个连通块,然后在代价和库存都满足不超限的情况下获得最大的收益
这种题容易想到枚举连通块的最高点(子树的根),然后跑有依赖的 DP
这个有依赖的 DP 就很有说法,其依赖关系形如:若父不选,则子不选
推广一下,若某子树根不选,则该子树不选
因此,可以直接把枚举出的子树拍平在 DFS 序列上,并尝试倒序转移
怎么搞呢?首先先二进制拆分让多重背包直接转 01 背包
再记录 \(f_{i,j}\) 表示转移到 dfn 序为 \(i\) 的结点,容量为 \(j\) 的最大值
考虑转移,核心就是决策 dfn 序为 \(i\) 的结点是否入选
入选非常容易,直接从 \(f_{i+1}\) 转移过来
不入选更容易,根据上面的依赖性分析,我们直接略过这个结点的子树,从 \(f_{i+sz[rev[i]]}\) 转移即可
时间复杂度 \(O(\text{不能过})\),咳咳准确来说是 \(O(T \times n^2 \times m \times \log V)\)
然后,就有了如下场景:
墨鱼:你看 \(O(n^2 \times m \times \log V)\) 是不是过不了
云落:(作沉思状)
墨鱼:别跟我说卡常,反正就是过不了
云落:嗯,好
墨鱼:你看前面这个枚举子树的操作,你想到了什么?
云落:嗯……?(走神)
墨鱼:嗯!想到什么了?
云落:呃,哦!
墨鱼:想到什么了?
云落:(沉默)
墨鱼:点分治啊!
没错这个题就是把枚举子树根的操作改装成点分治这种神秘的东西,就可以草过去咯!
(有点神秘)
像树上这种找连通块问题,墨鱼表示点分治是非常好用的,比如 首都 这道题目
因为它保证了跨子树的信息要么一定已经被计算过了,要么就是跨子树信息总是不能对答案造成贡献
时间复杂度 \(O(\text{能过})\),咳咳准确来说是 \(O(T \times n \log n \times m \log V)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=505,M=4e3+5,INF=2e9;
int n,m,w[N],c[N],d[N],head[N],tot,ans;
struct Edge{int to,nxt,val;}e[N<<1];
int siz[N];bool vis[N];int L[N],tim,rev[N],R[N];
int f[N][M];struct node{int v,w;}a[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void getsz(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
getsz(v,u);siz[u]+=siz[v];
}
return;
}
inline void getzx(int u,int fa,int rt,int &mn,int &g){
int sub=siz[rt]-siz[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
sub=max(sub,siz[v]);
getzx(v,u,rt,mn,g);
}
if(sub<mn)mn=sub,g=u;
return;
}
inline void dfs(int u,int fa){
L[u]=++tim;rev[tim]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
dfs(v,u);
}
R[u]=tim;
return;
}
inline void divide(int u){
getsz(u,0);
int mn=INF,g=0;
getzx(u,0,u,mn,g);vis[g]=true;
memset(L,0,sizeof(L));tim=0;
memset(rev,0,sizeof(rev));memset(R,0,sizeof(R));
dfs(g,0);
for(int i=tim;i>=1;i--){
int nd=rev[i],lim=d[nd]-1,idx=0,x=1;
while(x<=lim){
a[++idx]={w[nd]*x,c[nd]*x};
lim-=x,x<<=1;
}
if(lim)a[++idx]={w[nd]*lim,c[nd]*lim};
for(int j=m;j>=c[nd];j--)f[i][j]=f[i+1][j-c[nd]]+w[nd];
for(int x=1;x<=idx;x++)
for(int j=m;j>=a[x].w;j--)
f[i][j]=max(f[i][j],f[i][j-a[x].w]+a[x].v);
for(int j=0;j<=m;j++)f[i][j]=max(f[i][j],f[R[rev[i]]+1][j]);
}
ans=max(ans,f[1][m]);
for(int i=1;i<=tim;++i)
for(int j=0;j<=m;++j)
f[i][j]=0;
for(int i=head[g];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
divide(v);
}
return;
}
inline void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++)cin>>d[i];
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
divide(1);
cout<<ans<<'\n';
return;
}
inline void clr(){
memset(head,0,sizeof(head));tot=1;
memset(vis,false,sizeof(vis));
ans=0;
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)solve(),clr();
return 0;
}
T4
不会(恼)
T5
无聊的贪心,二分答案显然
问题转化为尽可能多的选择合法路径,并判定个数是否 \(\ge m\)
观察发现,对于 \((u,fa)\) 的边来说,至多只有一个 \(u\) 的儿子 \(v\) 可以使用
所以记 \(dis_v\) 表示 \(v\) 子树内已经处理完毕并且可以向上转移的路径长度
在 \(u\) 这一层中,直接把所有 \(v\) 都塞到 multiset 中。先内部消化(即两两配对。具体地,从小到大遍历,用 lower_bound 去找可以配对的 \((dis_v,dis_{v'})\)。然后在剩余未配对成功的 \(dis_v\) 中选取一个最大值,作为代表贡献给 \(dis_u\),参与更高一层的路径信息处理
(日常不会用 multiset,菜完了简直)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+5,INF=1e9;
int n,m,head[N],tot;
struct Edge{int to,nxt,val;}e[N<<1];
int dis[N];multiset<int> S;
inline void add(int u,int v,int w){
e[++tot]={v,head[u],w};head[u]=tot;
return;
}
inline int dfs(int u,int fa,int lim){
int res=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
res+=dfs(v,u,lim);
}
S.clear();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].val;
if(v==fa)continue;
dis[v]+=w;
if(dis[v]>=lim)res++;
else S.insert(dis[v]);
}
dis[u]=0;
while(!S.empty()){
int x=*S.begin();S.erase(S.begin());
auto it=S.lower_bound(lim-x);
if(it!=S.end())res++,S.erase(it);
else dis[u]=max(dis[u],x);
}
return res;
}
inline bool check(int mid){
for(int i=1;i<=n;i++)dis[i]=0;
return dfs(1,0,mid)>=m;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n-1;i++){
int u,v,w;cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
}
int l=1,r=INF,ans=1;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))l=mid+1,ans=mid;
else r=mid-1;
}
cout<<ans<<'\n';
return 0;
}
T6
板
点击查看代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int maxn=1e5+10;
int n,m,rt,p,a[maxn];
vector<int> G[maxn];
int fa[maxn],son[maxn],dep[maxn],sz[maxn];
int dfn[maxn],tim,Top[maxn],rev[maxn];
struct Segment_tree{
struct node{
int l,r,sum,tag;
}tr[maxn<<2];
void pushup(int u){
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
return;
}
void pushdown(int u){
if(tr[u].tag==0){
return;
}
int k=tr[u].tag;
tr[u<<1].sum=(tr[u<<1].sum+k*(tr[u<<1].r-tr[u<<1].l+1)%p)%p;
tr[u<<1|1].sum=(tr[u<<1|1].sum+k*(tr[u<<1|1].r-tr[u<<1|1].l+1)%p)%p;
tr[u<<1].tag=(tr[u<<1].tag+k)%p;
tr[u<<1|1].tag=(tr[u<<1|1].tag+k)%p;
tr[u].tag=0;
return;
}
void build(int u,int l,int r){
tr[u].l=l;
tr[u].r=r;
if(l==r){
tr[u].sum=rev[l]%p;
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
return;
}
void modify(int u,int ql,int qr,int k){
int l=tr[u].l,r=tr[u].r;
if(ql<=l&&qr>=r){
tr[u].sum=(tr[u].sum+k*(r-l+1)%p)%p;
tr[u].tag=(tr[u].tag+k)%p;
return;
}
pushdown(u);
int mid=l+r>>1;
if(ql<=mid){
modify(u<<1,ql,qr,k);
}
if(qr>mid){
modify(u<<1|1,ql,qr,k);
}
pushup(u);
return;
}
int query(int u,int ql,int qr){
int l=tr[u].l,r=tr[u].r;
if(ql<=l&&qr>=r){
return tr[u].sum%p;
}
pushdown(u);
int mid=l+r>>1,res=0;
if(ql<=mid){
res=(res+query(u<<1,ql,qr))%p;
}
if(qr>mid){
res=(res+query(u<<1|1,ql,qr))%p;
}
return res%p;
}
}Tr;
inline void dfs1(int u,int fath){
fa[u]=fath;
dep[u]=dep[fath]+1;
sz[u]=1;
int mx=-1;
for(int v:G[u]){
if(v==fath){
continue;
}
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>mx){
mx=sz[v];
son[u]=v;
}
}
return;
}
inline void dfs2(int u,int tp){
dfn[u]=++tim;
Top[u]=tp;
rev[tim]=a[u];
if(!son[u]){
return;
}
dfs2(son[u],tp);
for(int v:G[u]){
if(v==fa[u]||v==son[u]){
continue;
}
dfs2(v,v);
}
return;
}
inline void update_chain(int u,int v,int w){
w%=p;
while(Top[u]!=Top[v]){
if(dep[Top[u]]<dep[Top[v]]){
swap(u,v);
}
Tr.modify(1,dfn[Top[u]],dfn[u],w);
u=fa[Top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
Tr.modify(1,dfn[u],dfn[v],w);
return;
}
inline int query_chain(int u,int v){
int res=0;
while(Top[u]!=Top[v]){
if(dep[Top[u]]<dep[Top[v]]){
swap(u,v);
}
res=(res+Tr.query(1,dfn[Top[u]],dfn[u]))%p;
u=fa[Top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
res=(res+Tr.query(1,dfn[u],dfn[v]))%p;
return res;
}
inline void update_subtree(int u,int k){
k%=p;
Tr.modify(1,dfn[u],dfn[u]+sz[u]-1,k);
return;
}
inline int query_subtree(int u){
int res=Tr.query(1,dfn[u],dfn[u]+sz[u]-1)%p;
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>rt>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(rt,0);
dfs2(rt,rt);
Tr.build(1,1,n);
for(int k=1;k<=m;k++){
int opt;
cin>>opt;
if(opt==1){
int x,y,z;
cin>>x>>y>>z;
update_chain(x,y,z);
}else if(opt==2){
int x,y;
cin>>x>>y;
int ans=query_chain(x,y)%p;
cout<<ans<<endl;
}else if(opt==3){
int x,z;
cin>>x>>z;
update_subtree(x,z);
}else if(opt==4){
int x;
cin>>x;
int ans=query_subtree(x)%p;
cout<<ans<<endl;
}
}
return 0;
}
T7
板 \(\times 2\)
点击查看代码
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=1e6+5,INF=0x3f3f3f3f;
int n,m,rt,w[N],head[N],tot;
struct Edge{int to,nxt;}e[N<<1];
struct Matrix{
int a[2][2];
friend Matrix operator * (Matrix A,Matrix B){
Matrix C;
C.a[0][0]=C.a[0][1]=C.a[1][0]=C.a[1][1]=-INF;
C.a[0][0]=max(A.a[0][0]+B.a[0][0],A.a[0][1]+B.a[1][0]);
C.a[0][1]=max(A.a[0][0]+B.a[0][1],A.a[0][1]+B.a[1][1]);
C.a[1][0]=max(A.a[1][0]+B.a[0][0],A.a[1][1]+B.a[1][0]);
C.a[1][1]=max(A.a[1][0]+B.a[0][1],A.a[1][1]+B.a[1][1]);
return C;
}
}A[N],B[N];
int dep[N],son[N],siz[N],lsiz[N];
int f[N][2],g[N][2],trfa[N],ls[N],rs[N];
int stk[N],tp;
bool vis[N];
inline void add(int u,int v){e[++tot]={v,head[u]};head[u]=tot;return;}
inline void dfs(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
lsiz[u]=siz[u]-siz[son[u]];
return;
}
inline void init(int u,int fa){
if(son[u])init(son[u],u);
g[u][1]=w[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||v==son[u])continue;
init(v,u);
g[u][0]+=max(f[v][0],f[v][1]);
g[u][1]+=f[v][0];
}
f[u][0]=g[u][0]+max(f[son[u]][0],f[son[u]][1]);
f[u][1]=g[u][1]+f[son[u]][0];
return;
}
inline void pushup(int u){
B[u]=A[u];
if(ls[u])B[u]=B[ls[u]]*B[u];
if(rs[u])B[u]=B[u]*B[rs[u]];
return;
}
inline int Sbuild(int l,int r){
if(l>r)return 0;
int sum=0;
for(int i=l;i<=r;i++)sum+=lsiz[stk[i]];
int tmp=0;
for(int i=l;i<=r;i++){
tmp+=lsiz[stk[i]];
if(tmp*2>=sum){
int u=stk[i],lch=Sbuild(l,i-1),rch=Sbuild(i+1,r);
ls[u]=lch;rs[u]=rch; trfa[lch]=trfa[rch]=u;
pushup(u);
return u;
}
}
return 0;
}
inline int build(int u){
for(int x=u;x;x=son[x])vis[x]=true;
for(int x=u;x;x=son[x]){
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
trfa[build(v)]=x;
}
}
tp=0;
for(int x=u;x;x=son[x])stk[++tp]=x;
return Sbuild(1,tp);
}
inline int getmx2(int u){return max(B[u].a[0][0],B[u].a[0][1]);}
inline int getmx1(int u){return max(getmx2(u),B[u].a[1][0]);}
inline void upd(int u,int v){
A[u].a[1][0]+=(v-w[u]);w[u]=v;
for(int x=u;x;x=trfa[x]){
if(trfa[x]&&ls[trfa[x]]!=x&&rs[trfa[x]]!=x){
A[trfa[x]].a[0][0]-=getmx1(x);
A[trfa[x]].a[0][1]=A[trfa[x]].a[0][0];
A[trfa[x]].a[1][0]-=getmx2(x);
pushup(x);
A[trfa[x]].a[0][0]+=getmx1(x);
A[trfa[x]].a[0][1]=A[trfa[x]].a[0][0];
A[trfa[x]].a[1][0]+=getmx2(x);
}else pushup(x);
}
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
add(u,v);add(v,u);
}
dfs(1,-1);init(1,-1);
for(int i=1;i<=n;i++){
A[i].a[0][0]=A[i].a[0][1]=g[i][0];
A[i].a[1][0]=g[i][1];A[i].a[1][1]=-INF;
}
rt=build(1);
int lst=0;
while(m--){
int u,v;cin>>u>>v;u^=lst;
upd(u,v);lst=getmx1(rt);
cout<<lst<<endl;
}
return 0;
}
T8
花絮时间
云落:“非板,好耶!”
(省略一个看题过程)
系统提示:云落撤回了一条消息
正片开始
把树上对应的那一层抓出来,子树的限制直接转化为 dfn 的限制
对于符合所有限制的结点,直接哈希并异或起来判断即可
(无聊)
点击查看代码
#include<bits/stdc++.h>
#define vi vector<int>
#define pb push_back
#define lwbd lower_bound
#define upbd upper_bound
using namespace std;
const int N=5e5+1;
int n,m,head[N],tot;string s;
struct Edge{int to,nxt;}e[N<<1];
int L[N],tim,rev[N],dep[N],R[N];
int mx;vi vec[N],sum[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void dfs(int u,int fa){
L[u]=++tim;rev[tim]=u;dep[u]=dep[fa]+1;
mx=max(mx,dep[u]);vec[dep[u]].pb(L[u]);
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa)continue;
dfs(v,u);
}
R[u]=tim;
return;
}
inline void solve(){
int v,h;cin>>v>>h;
int l=lwbd(vec[h].begin(),vec[h].end(),L[v])-vec[h].begin();
int r=upbd(vec[h].begin(),vec[h].end(),R[v])-vec[h].begin()-1;
if(r<0){cout<<"Yes\n";return;}
int ans=(l==0?sum[h][r]:sum[h][r]^sum[h][l-1]);
int num=__builtin_popcount(ans);
cout<<(num==1||num==0?"Yes\n":"No\n");
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=2;i<=n;i++){
int x;cin>>x;
add(i,x),add(x,i);
}
cin>>s;s=' '+s;
dfs(1,0);
for(int d=1;d<=mx;d++){
sum[d].pb(1<<(s[rev[vec[d][0]]]-'a'));
int _size=vec[d].size();
for(int i=1;i<=_size-1;i++){
int val=(1<<(s[rev[vec[d][i]]]-'a'));
sum[d].pb(sum[d][i-1]^val);
}
}
while(m--)solve();
return 0;
}
T9
板 \(\times 3\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=105,K=1e7+5,INF=2e9;
int n,m,Q[M],head[N],tot,ans[M];
struct Edge{int to,nxt,val;}e[N<<1];
int siz[N];bool vis[N];
int dis[N],cnt,t[N],tol;bool mrk[K];
inline void add(int u,int v,int w){
e[++tot]={v,head[u],w};head[u]=tot;
return;
}
inline void getsz(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
getsz(v,u);siz[u]+=siz[v];
}
return;
}
inline void getzx(int u,int fa,int rt,int &mn,int &g){
int sub=siz[rt]-siz[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
sub=max(sub,siz[v]);
getzx(v,u,rt,mn,g);
}
if(sub<mn)mn=sub,g=u;
return;
}
inline void dfs(int u,int fa,int depth){
if(depth<=1e7)t[++tol]=depth;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].val;
if(v==fa||vis[v])continue;
dfs(v,u,depth+w);
}
return;
}
inline void solve(int u){
getsz(u,0);
int mn=INF,g=0;
getzx(u,0,u,mn,g);vis[g]=true;
mrk[0]=true;cnt=0;dis[++cnt]=0;
for(int i=head[g];i;i=e[i].nxt){
int v=e[i].to,w=e[i].val;
if(vis[v])continue;
tol=0;dfs(v,g,w);
for(int x=1;x<=tol;x++)
for(int y=1;y<=m;y++)
if(Q[y]>=t[x])ans[y]|=mrk[Q[y]-t[x]];
for(int x=1;x<=tol;x++){
dis[++cnt]=t[x];
if(t[x]<=1e7)mrk[t[x]]=true;
}
}
for(int i=1;i<=cnt;i++)
if(dis[i]<=1e7)mrk[dis[i]]=false;
for(int i=head[g];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
solve(v);
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n-1;i++){
int u,v,w;cin>>u>>v>>w;
add(u,v,w),add(v,u,w);
}
for(int i=1;i<=m;i++)cin>>Q[i];
solve(1);
for(int i=1;i<=m;i++)cout<<(ans[i]?"AYE\n":"NAY\n");
return 0;
}
T10
板 \(\times 10086\),但是不会
被墨鱼拯救的第二题,但暂时不想写算法梳理
先挖个坑,以后再填
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,K=18,INF=0x3f3f3f3f;
int n,Q,a[N],head[N],tot;
struct Edge{int to,nxt;}e[N<<1];
int dep[N],dfn[N<<1],tim,bra[N<<1];
int siz[N],dfa[N];bool vis[N];
struct Sparse_table{
int lg[N<<1],f[N<<1][K];
inline int MIN(int x,int y){return dep[x]<dep[y]?x:y;}
inline void init(){
lg[0]=-1;
for(int i=1;i<=tim;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=tim;i++)f[i][0]=bra[i];
for(int j=1;j<=lg[tim];j++)
for(int i=1;i+(1<<j)<=tim;i++)
f[i][j]=MIN(f[i][j-1],f[i+(1<<(j-1))][j-1]);
return;
}
inline int ask(int l,int r){
int p=lg[r-l+1];
return MIN(f[l][p],f[r-(1<<p)+1][p]);
}
}ST;
struct Segment_tree{
int rt[N],cnt=0;
struct node{int l,r,sum;}tr[N<<6];
inline void pushup(int u){
tr[u].sum=(tr[tr[u].l].sum+tr[tr[u].r].sum);
return;
}
inline void modify(int &u,int l,int r,int pos,int v){
if(!u)u=++cnt;
if(l==r){tr[u].sum+=v;return;}
int mid=(l+r)>>1;
if(pos<=mid)modify(tr[u].l,l,mid,pos,v);
else modify(tr[u].r,mid+1,r,pos,v);
pushup(u);
}
inline int query(int u,int l,int r,int ql,int qr){
if(!u)return 0;
if(ql<=l&&r<=qr)return tr[u].sum;
int mid=(l+r)>>1;
if(qr<=mid)return query(tr[u].l,l,mid,ql,qr);
if(ql>mid)return query(tr[u].r,mid+1,r,ql,qr);
return query(tr[u].l,l,mid,ql,mid)+query(tr[u].r,mid+1,r,mid+1,qr);
}
}sgt1,sgt2;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void dfs(int u,int fath){
dep[u]=dep[fath]+1;dfn[u]=++tim;
bra[tim]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fath)continue;
dfs(v,u);bra[++tim]=u;
}
return;
}
inline int LCA(int x,int y){
if(x==y)return x;
if(dfn[x]>dfn[y])swap(x,y);
x=dfn[x];y=dfn[y];
return ST.ask(x,y);
}
inline int getdis(int x,int y){return dep[x]+dep[y]-dep[LCA(x,y)]*2;}
inline void getsz(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
getsz(v,u);siz[u]+=siz[v];
}
return;
}
inline void getzx(int u,int fa,int rt,int &mn,int &g){
int sub=siz[rt]-siz[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa||vis[v])continue;
sub=max(sub,siz[v]);
getzx(v,u,rt,mn,g);
}
if(sub<mn)mn=sub,g=u;
return;
}
inline void divide(int u,int DFA){
getsz(u,0);
int mn=INF,g=0;
getzx(u,0,u,mn,g);vis[g]=true;dfa[g]=DFA;
for(int i=head[g];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
divide(v,g);
}
return;
}
inline void upd(int u,int v){
int now=u;
while(now){
sgt1.modify(sgt1.rt[now],0,n-1,getdis(now,u),v);
if(dfa[now])sgt2.modify(sgt2.rt[now],0,n-1,getdis(dfa[now],u),v);
now=dfa[now];
}
return;
}
inline int ask(int u,int k){
int now=u,pre=0,res=0;
while(now){
if(getdis(now,u)<=k){
res+=sgt1.query(sgt1.rt[now],0,n-1,0,k-getdis(now,u));
if(pre)res-=sgt2.query(sgt2.rt[pre],0,n-1,0,k-getdis(now,u));
}
pre=now;now=dfa[now];
}
return res;
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>Q;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n-1;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
dfs(1,0);ST.init();divide(1,0);
for(int i=1;i<=n;i++)upd(i,a[i]);
int lst=0;
while(Q--){
int opt,x,y;cin>>opt>>x>>y;
x^=lst,y^=lst;
if(opt==0)cout<<(lst=ask(x,y))<<'\n';
else upd(x,y-a[x]),a[x]=y;
}
return 0;
}
T11
还是不会,菜完了
STO 软软 orz
后记
世界孤立我任它奚落
完结撒花!

浙公网安备 33010602011771号