2022/10/18 总结
反转 DAG
-
预期得分 \(\mathtt{30pts+}\),实得 \(\mathtt{54pts}\);
-
暴力思路是暴力跑图,找出每个环的最小边,再求最大值;有一个玄学优化是每次扫完一个环就把最小的那条边标记为不可访问(因为答案必定 \(\ge\) 它,所以这条边不会再产生贡献)
-
说实话想到过二分,但不知道之后怎么做,然后就放弃了……
Solution
- 二分答案,枚举每次最大的 \(k\),然后把原图中所有边权 \(\le k\) 的边都删掉,再跑一遍原图,看是不是 \(DAG\);
AC code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
s=s*10+int(ch-'0');
ch=getchar();
}
return s*f;
}
const int N=1e5+10;
int n,q,rt;
int l[N],r[N],ind[N],id[N],v[N],cnt=0;
int head[N],ver[N],nxt[N],tot=0;
int f[25][N],dep[N];
struct memr{
int l,r;
int mn;
}tr[N<<3];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x],head[x]=tot;
return ;
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=24;i>-1;--i)
if(dep[f[i][x]]>=dep[y])
x=f[i][x];
if(x==y) return x;
return 0;
}
int A(int x,int h){
for(int i=24;i>-1;--i)
if(dep[f[i][x]]>=h)
x=f[i][x];
return x;
}
void dfs(int p){
ind[++cnt]=p;
id[p]=cnt;
l[p]=cnt;
dep[p]=dep[f[0][p]]+1;
for(int i=1;i<25 && f[i-1][p];++i)
f[i][p]=f[i-1][f[i-1][p]];
for(int i=head[p];i;i=nxt[i])
dfs(ver[i]);
r[p]=cnt;
return ;
}
void pushup(int p){
tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn);
return ;
}
void mix(int p,int x,int v){
if(x==tr[p].l && tr[p].r==x){
tr[p].mn=v;
return ;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid) mix(p<<1,x,v);
else mix(p<<1|1,x,v);
pushup(p);
return ;
}
int ask(int p,int l,int r){
if(l<=tr[p].l && tr[p].r<=r)
return tr[p].mn;
int cnt=0x3f3f3f3f;
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) cnt=min(cnt,ask(p<<1,l,r));
if(mid<r) cnt=min(cnt,ask(p<<1|1,l,r));
return cnt;
}
void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;
if(l==r){
tr[p].mn=v[ind[l]];
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
return ;
}
int main(){
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;++i){
f[0][i]=read(),v[i]=read();
add(f[0][i],i);
}
rt=1;
dfs(1);
build(1,1,n);
char opt;
int x,y,ans;
while(q--){
cin>>opt;
x=read();
if(opt=='V'){
y=read();
mix(1,id[x],y);
}
else{
if(opt=='E')
rt=x;
else{
int a=LCA(rt,x);
if(x==rt)
ans=tr[1].mn;
else if(a==x){
int z=A(rt,dep[x]+1);
ans=min(ask(1,1,l[z]-1),ask(1,r[z]+1,n));
}
else ans=ask(1,l[x],r[x]);
printf("%d\n",ans);
}
}
}
return 0;
}
丑陋的树
- 期望得分 \(\mathtt{100pts}\),实际得分 \(\mathtt{100pts}\);
Solution
-
搞一下 \(DFS\) 序,然后放到线段树上去;
-
考虑换根操作,画一下图就会知道,只有根 \(x\) 的直系祖先会受换根操作影响,其他节点直接在原树上查子树就行了。而对于 \(x\) 的直系祖先,它的新子树是除了包含了 \(x\) 的那棵子树以外的所有点(包括它原来的父节点);
-
还有 \(x\) 的子树是全体点的集合;
AC code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
s=s*10+int(ch-'0');
ch=getchar();
}
return s*f;
}
const int N=1e5+10;
int n,q,rt;
int l[N],r[N],ind[N],id[N],v[N],cnt=0;
int head[N],ver[N],nxt[N],tot=0;
int f[25][N],dep[N];
struct memr{
int l,r;
int mn;
}tr[N<<3];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x],head[x]=tot;
return ;
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=24;i>-1;--i)
if(dep[f[i][x]]>=dep[y])
x=f[i][x];
if(x==y) return x;
return 0;
}
int A(int x,int h){
for(int i=24;i>-1;--i)
if(dep[f[i][x]]>=h)
x=f[i][x];
return x;
}
void dfs(int p){
ind[++cnt]=p;
id[p]=cnt;
l[p]=cnt;
dep[p]=dep[f[0][p]]+1;
for(int i=1;i<25 && f[i-1][p];++i)
f[i][p]=f[i-1][f[i-1][p]];
for(int i=head[p];i;i=nxt[i])
dfs(ver[i]);
r[p]=cnt;
return ;
}
void pushup(int p){
tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn);
return ;
}
void mix(int p,int x,int v){
if(x==tr[p].l && tr[p].r==x){
tr[p].mn=v;
return ;
}
int mid=(tr[p].l+tr[p].r)>>1;
if(x<=mid) mix(p<<1,x,v);
else mix(p<<1|1,x,v);
pushup(p);
return ;
}
int ask(int p,int l,int r){
if(l<=tr[p].l && tr[p].r<=r)
return tr[p].mn;
int cnt=0x3f3f3f3f;
int mid=(tr[p].l+tr[p].r)>>1;
if(l<=mid) cnt=min(cnt,ask(p<<1,l,r));
if(mid<r) cnt=min(cnt,ask(p<<1|1,l,r));
return cnt;
}
void build(int p,int l,int r){
tr[p].l=l,tr[p].r=r;
if(l==r){
tr[p].mn=v[ind[l]];
return ;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
return ;
}
int main(){
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;++i){
f[0][i]=read(),v[i]=read();
add(f[0][i],i);
}
rt=1;
dfs(1);
build(1,1,n);
char opt;
int x,y,ans;
while(q--){
cin>>opt;
x=read();
if(opt=='V'){
y=read();
mix(1,id[x],y);
}
else{
if(opt=='E')
rt=x;
else{
int a=LCA(rt,x);
if(x==rt)
ans=tr[1].mn;
else if(a==x){
int z=A(rt,dep[x]+1);
ans=min(ask(1,1,l[z]-1),ask(1,r[z]+1,n));
}
else ans=ask(1,l[x],r[x]);
printf("%d\n",ans);
}
}
}
return 0;
}