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]\) 中的鱼两两之间(包括自己和自己)的最大曼哈顿距离,即:
由于鱼的记忆只有七秒,难以进行大量的运算,所以它们想请你帮忙给出答案。
对于 \(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\)。
题解
先套路的转化为切比雪夫距离,那么我们的式子就变成了:
发现只和一个值有关,所以只需要维护区间新的 \(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;
}