11.22模拟赛
T1
给定一棵 \(n\) 个点的树,点有颜色,问有哪些 \(u\) 满足,对于任意的 \(v\),路径 \((u,v)\) 上不出现重复颜色。
对于所有数据,满足 \(1 \leq n \leq 2 \times 10^5, 1 \leq c_i \leq n\)。
题解
考虑用样的颜色会使得那些点不能成为答案,思考之后我们可以得到这个性质,现在我们以 \(1\) 为根:
- 对于相同颜色的点考虑,如果这个点的子树有相同颜色点,那么这个点的非子树节点无解。
- 如果这个点的非子树节点有相同颜色点,那么这个点的子树节点无解。
但是会有一个 corner case,就是根节点的判断没有非子树的部分,所以会错。考虑再用 \(2\) 为根跑一次就行了。
为了处理上面的判断,我们可以用两个树状数组维护。
code:
#include<bits/stdc++.h>
#define pb push_back
#define int long long
#define lbt(x) (x&(-x))
#define m(a) memset(a,0,sizeof(a))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=5e5+10;
int n,m,k,T,a[N],t[N],t1[N],sum,dfn[N],tot,out[N],res[N];
vector<int> g[N],col[N],ans;
void upd(int i,int x){while(i<N){t[i]+=x;i+=lbt(i);}}
int sc(int i){int ans=0;while(i>0){ans+=t[i];i-=lbt(i);}return ans;}
int query(int l,int r){return sc(r)-sc(l-1);}
void upd1(int i,int x){while(i<N){t1[i]+=x;i+=lbt(i);}}
int sc1(int i){int ans=0;while(i>0){ans+=t1[i];i-=lbt(i);}return ans;}
void add1(int l,int r,int x){upd1(l,x);upd1(r+1,-x);}
void dfs(int u,int fa){
dfn[u]=++tot;
for(int v:g[u]){
if(v==fa) continue;
dfs(v,u);
}out[u]=tot;
}
void solve(int x){
tot=0;dfs(x,0);m(t1);m(t);
rep(i,1,n){
for(int u:col[i]) upd(dfn[u],1);
for(int u:col[i]){
int x=query(dfn[u]+1,out[u]);
if(x>=1){
add1(1,dfn[u],1);add1(out[u]+1,n,1);
}if(col[i].size()-x>1){
add1(dfn[u],out[u],1);
}
}for(int u:col[i]) upd(dfn[u],-1);
}rep(i,1,n){
if(sc1(dfn[i])>0){
res[i]=1;
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("problem.in","r",stdin);
freopen("problem.out","w",stdout);
cin>>n;
rep(i,1,n){
int x;cin>>x;
col[x].pb(i);
}rep(i,1,n-1){
int u,v;cin>>u>>v;
g[u].pb(v);g[v].pb(u);
}solve(1);solve(2);
rep(i,1,n){
if(res[i]==0){
ans.pb(i);sum++;
}
}cout<<sum<<'\n';
for(int u:ans){
cout<<u<<" ";
}
return 0;
}
T2
荷塘是一个平面直角坐标系,其中有 \(n\) 条鱼,编号为 \(1, 2, \ldots, n\),位置用一个点 \((x_i, y_i)\) 表示(可以重复)。它们听路过的米奇提到 \(\text{“}\) 平面最远点对 \(\text{”}\),于是想亲身实践一下——进行 \(m\) 次操作,每次为以下两种之一:
- 充分发扬鱼类智慧,将编号在 \([l, r]\) 中的鱼的位置绕原点逆时针旋转 \(90^\circ\)。
- 询问编号在 \([l, r]\) 中的鱼两两之间(包括自己和自己)的最大曼哈顿距离,即:
\[\max_{l \leq i \leq j \leq r} \{ |x_i - x_j| + |y_i - y_j| \}
\]
由于鱼的记忆只有七秒,难以进行大量的运算,所以它们想请你帮忙给出答案。
对于 \(100\%\) 的数据,满足 \(1 \leq n, m \leq 2 \times 10^5, 1 \leq x_i, y_i \leq 10^8, 1 \leq l \leq r \leq n\)。
题解
先套路的转化为切比雪夫距离,那么我们的式子就变成了:
\[\max_{l \leq i \leq j \leq r} \{ \max(|x_i - x_j| , |y_i - y_j|) \}
\]
发现只和一个值有关,所以只需要维护区间新的 \(x,y\) 的最大最小值就行了。现在考虑怎么做翻转操作。我们同时维护这些区间翻转 \(0\degree,90\degree,180\degree,270\degree\) 的值,翻转之后值轮换一下就好了,直接用线段树维护即可。
code:
#include<bits/stdc++.h>
#define fi first
#define se second
#define ls (p<<1)
#define rs (p<<1|1)
#define int long long
#define pii pair<int,int>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int N=5e5+10;
int n,m,q,T;pii a[N];
vector<int> g[N];
struct tree{
int u,l,d,r,laz;
friend tree operator+(tree a,tree b){
tree tmp;
tmp.u=max(a.u,b.u),tmp.l=min(a.l,b.l);
tmp.d=min(a.d,b.d),tmp.r=max(a.r,b.r);
return tmp;
}
}t[N<<2];
void build(int p,int l,int r){
if(l==r){
int x=a[l].fi,y=a[l].se;
t[p]={x+y,x-y,x+y,x-y,0};return;
}int mid=l+r>>1;
build(ls,l,mid);build(rs,mid+1,r);
t[p]=t[ls]+t[rs];
}
void solve(int p,int x){
tree tmp=t[p];
t[p].laz=(t[p].laz+x)%4;
while(x--){
t[p].u=tmp.r;t[p].l=-tmp.u;
t[p].d=tmp.l;t[p].r=-tmp.d;
tmp=t[p];
}
}
void pushdown(int p){
if(t[p].laz==0) return;
solve(ls,t[p].laz);solve(rs,t[p].laz);
t[p].laz=0;
}
void upd(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
solve(p,1);return;
}int mid=l+r>>1;pushdown(p);
if(x<=mid) upd(ls,l,mid,x,y);
if(y>mid) upd(rs,mid+1,r,x,y);
t[p]=t[ls]+t[rs];
}
tree query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y) return t[p];
int mid=l+r>>1;pushdown(p);
if(y<=mid) return query(ls,l,mid,x,y);
else if(x>mid) return query(rs,mid+1,r,x,y);
else return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("fish.in","r",stdin);
freopen("fish.out","w",stdout);
cin>>n>>q;
rep(i,1,n){
int x,y;cin>>x>>y;
a[i]={x,y};
}build(1,1,n);
while(q--){
int op,l,r;cin>>op>>l>>r;
if(op==1) upd(1,1,n,l,r);
else{
tree res=query(1,1,n,l,r);
cout<<max(res.u-res.d,res.r-res.l)<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号