【CSP-S 2019爆零模拟】题解
太菜爆零滚粗了
T1:
考试的时候并没有一眼想到线段树分治的暴力
想了想似乎需要在树上乱搞些东西
但不是很明确就去推
结果还真的是
具体的说考虑建出树来
对每个点维护到父亲的边在多少个奇环偶环里
对于一条树边
如果被所有奇环包含而不被偶环包含就可以
如果被偶环包含那么怎么删都不行
对于返祖边
如果是形成了奇环且只有这个奇环就可以了
然后就完了
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ob==ib)?EOF:*ib++;
}
#define gc getchar
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
#define poly vector<int>
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=2000005;
struct edge{
int u,v,s,d,in;
edge(){u=v=s=d=in=0;}
}E[N];
int sip[N],db[N],dep[N],tot;
int vis[N];
int n,m;
vector<pii >e[N];
void dfs(int u){
vis[u]=1;
for(pii &x:e[u]){
int v=x.fi;
if(vis[v])continue;
dep[v]=dep[u]+1,dfs(v);
E[x.se].in=1;
}
}
void dfs2(int u,int fa){
vis[u]=1;
for(pii &x:e[u]){
int v=x.fi;
if(v==fa||!E[x.se].in)continue;
dfs2(v,u);
sip[u]+=sip[v],db[u]+=db[v];
E[x.se].s=sip[v],E[x.se].d=db[v];
}
}
vector<int> ans;
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
E[i].u=u,E[i].v=v;
e[u].pb(pii(v,i)),e[v].pb(pii(u,i));
}
for(int i=1;i<=n;i++)if(!vis[i])dfs(i);
for(int i=1;i<=m;i++){
if(E[i].in)continue;
int u=E[i].u,v=E[i].v;
if(dep[u]<dep[v])swap(u,v);
if((dep[u]-dep[v]+1)&1)E[i].s=1,tot++,sip[u]++,sip[v]--;
else E[i].d=1,db[u]++,db[v]--;
}
if(!tot){
cout<<m<<'\n';
for(int i=1;i<=m;i++)cout<<i<<" ";return 0;
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)if(!vis[i])dfs2(i,0);
for(int i=1;i<=m;i++){
if(E[i].in)
{if(E[i].s==tot&&!E[i].d)ans.pb(i);}
else if(tot==1&&E[i].s)ans.pb(i);
}
cout<<ans.size()<<'\n';
for(int &x:ans)cout<<x<<" ";
}
T2:
垃圾搬题人
搬个模板原题
给最低的分大暴力
不给
不给离线
搬题人nmsl
显然的发现实际上就是插入一个新后缀,在线维护后缀数组
支持比较
但是需要做到查询
考场上由于不会后缀平衡树
想的是用直接维护,比较也想到了用实数取
但是直接并不能保证取的树高
会炸精度
考完就想到可以重构
于是写个替罪羊树就完了
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ob==ib)?EOF:*ib++;
}
inline void readstring(char *s){
char ch=gc();
int top=0;
while(!(('a'<=ch&&ch<='z')||('A'<=ch&&ch<='Z')))ch=gc();
while((('a'<=ch&&ch<='z')||('A'<=ch&&ch<='Z')))s[++top]=ch,ch=gc();
}
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define pic pair<int,char>
#define fi first
#define se second
#define pb push_back
#define cs const
#define ull unsigned long long
#define puu pair<ull,ull>
#define bg begin
#define poly vector<int>
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=1000005;
int pos[N],s[N],n,m,len,rt,pp[N],bel[N<<3];
double val[N];
cs double lim=1e18;
namespace SBT{
cs double alpha=0.75;
int tot,lc[N<<3],rc[N<<3],siz[N<<3],stk[N<<3],id[N<<3],top;
void collect(int u){
if(!u)return;
collect(lc[u]),stk[++top]=u,collect(rc[u]);
}
int build(int l,int r,double vl,double vr){
if(l>r)return 0;
int mid=(l+r)>>1,u=stk[mid];
double vmid=(vl+vr)/2;
val[u]=vmid,lc[u]=rc[u]=0;
lc[u]=build(l,mid-1,vl,vmid),rc[u]=build(mid+1,r,vmid,vr);
siz[u]=siz[lc[u]]+siz[rc[u]]+1;return u;
}
inline int rebuild(int u,double l,double r){
top=0,collect(u);
return build(1,top,l,r);
}
inline bool comp(int a,int b){
return s[a]==s[b]?val[bel[a-1]]<=val[bel[b-1]]:s[a]<=s[b];
}
void insert(int &u,double l,double r,int p){
double mid=(l+r)/2;
if(!u){
u=++tot,val[u]=mid,siz[u]=1,id[tot]=p,bel[p]=tot;return;
}
if(alpha*siz[u]<max(siz[lc[u]],siz[rc[u]]))u=rebuild(u,l,r);
if(comp(p,id[u]))insert(lc[u],l,mid,p);
else insert(rc[u],mid+1,r,p);
siz[u]=siz[lc[u]]+siz[rc[u]]+1;
}
}
namespace Seg{
int mn[N<<2];
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
#define comp(a,b) (val[bel[(a)]]<=val[bel[(b)]])
inline void pushup(int u){
mn[u]=comp(pos[mn[lc]],pos[mn[rc]])?mn[lc]:mn[rc];
}
void build(int u,int l,int r){
if(l==r){mn[u]=l;return;}
build(lc,l,mid),build(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int p){
if(l==r)return;
if(p<=mid)update(lc,l,mid,p);
else update(rc,mid+1,r,p);
pushup(u);
}
int query(int u,int l,int r,int st,int des){
if(st<=l&&r<=des)return mn[u];
if(des<=mid)return query(lc,l,mid,st,des);
if(mid<st)return query(rc,mid+1,r,st,des);
int al=query(lc,l,mid,st,des),ar=query(rc,mid+1,r,st,des);
return comp(pos[al],pos[ar])?al:ar;
}
}
char ss[N];
int last,tp;
int main(){
n=read(),m=read(),len=read();
readstring(ss);
reverse(ss+1,ss+len+1);
for(int i=1;i<=len;i++)s[i]=ss[i]-'a'+1,SBT::insert(rt,0,lim,i);
for(int i=1;i<=n;i++)pos[i]=read();
Seg::build(1,1,n);
for(int i=1;i<=m;i++){
readstring(ss);
if(ss[1]=='C'){
int x=read(),p=read();
pos[x]=p,Seg::update(1,1,n,x);
}
else if(ss[1]=='Q'){
int l=read(),r=read();
cout<<(last=Seg::query(1,1,n,l,r))<<'\n';
}
else{
int c=(read()^last)+1;
s[++len]=c,SBT::insert(rt,0,lim,len);
}
}
}
T3:
之前雅礼集训考过
虽然也是原题
显然就是要把最大的子树中选一个适合的接到最小的子树
于是可以线段树合并维护
每次在线段树上找前驱后继就可以做到
但是人太蠢写了个二分多一个
还要考虑子树的补集的
其实可以直接在序上建主席树维护
但是人太蠢写了换根的做法
就需要维护子树合并的前后缀
而且这样不能直接维护到根的点
因为往下走会不断修改
所以还需要单独维护一个到根的差分一下
代码又臭又长又垃圾
而且会被菊花图和一个点给叉掉竟然没有被卡
而且细节贼多
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
inline char gc(){
static char ibuf[RLEN],*ib,*ob;
(ob==ib)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ob==ib)?EOF:*ib++;
}
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
#define ll long long
#define re register
#define pii pair<int,int>
#define pic pair<int,char>
#define fi first
#define se second
#define pb push_back
#define cs const
#define bg begin
#define poly vector<int>
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int N=100005;
char x;
int rt[N],fg,n;
namespace Seg{
cs int M=N*200;
int siz[M],lc[M],rc[M],tot;
#define mid ((l+r)>>1)
inline int copy(int u){
int now=++tot;
lc[now]=lc[u],rc[now]=rc[u],siz[now]=siz[u];return now;
}
inline void insert(int &u,int l,int r,int p){
u=copy(u),siz[u]++;
if(l==r)return;
if(p<=mid)insert(lc[u],l,mid,p);
else insert(rc[u],mid+1,r,p);
}
void merge(int &u,int r1,int r2){
if(!r1||!r2){u=r1+r2;return;}
u=++tot,siz[u]=siz[r1]+siz[r2];
merge(lc[u],lc[r1],lc[r2]);
merge(rc[u],rc[r1],rc[r2]);
}
int query(int u,int l,int r,int st,int des){
if(!u)return 0;
//if(fg)cerr<<u<<" "<<l<<" "<<r<<" "<<st<<" "<<des<<'\n';
if(st>des||st>r||des<l)return 0;
if(st<=l&&r<=des)return siz[u];
int res=0;
if(st<=mid)res+=query(lc[u],l,mid,st,des);
if(mid<des)res+=query(rc[u],mid+1,r,st,des);
return res;
}
void write(int u,int l,int r){
if(!u)return;
if(l==r)cout<<l<<" "<<siz[u]<<'\n';
write(lc[u],l,mid),write(rc[u],mid+1,r);
}
#undef mid
}
vector<int> e[N];
int str,siz[N],fa[N],in[N],ans[N],tort[N];
char y;
void dfs1(int u){
siz[u]=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u])continue;
dfs1(v),Seg::merge(rt[u],rt[u],rt[v]);
// cerr<<u<<" "<<v<<" "<<siz[v]<<'\n';
siz[u]+=siz[v];
}
Seg::insert(rt[u],1,n,siz[u]);
}
void getrt(int u){
int now=tort[u];
Seg::insert(now,1,n,siz[u]);
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u])continue;
tort[v]=now,getrt(v);
}
}
struct node{
int f,s,t,l;
node(int _f=0,int _s=0,int _t=0,int _l=0):f(_f),s(_s),t(_t),l(_l){}
friend inline bool operator <(cs node &a,cs node &b){
return a.f<b.f;
}
};
inline int solve(vector<node> now){
int n=now.size();
sort(now.bg(),now.end());
int del=now[n-1].f-now[0].f,pl,pr;
if(!del)return now[n-1].f;
if(del&1)pl=del/2,pr=del/2+1;
else pl=del/2,pr=del/2;
int l=0,r=::n,res=0;
while(l<=r){
int mid=(l+r)>>1;
if(Seg::query(now[n-1].s,1,::n,pl-mid+1,pr+mid-1)+Seg::query(now[n-1].t,1,::n,pl-mid+1+now[n-1].l,pr+mid-1+now[n-1].l)>0)r=mid-1,res=mid;
else l=mid+1;
}
int ret=max(now[n-1].f-(pl-res+1),now[0].f+(pl-res+1));
if(n>2)chemx(ret,now[n-2].f);
return ret;
}
void dfs2(int u,int rtfa){
if(in[u]==1){ans[u]=n-1;}
else {
vector<node> all;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u])continue;
all.pb(node(siz[v],rt[v],0,0));
}
if(fa[u])all.pb(node(n-siz[u],rtfa,tort[u],siz[u]));
ans[u]=solve(all);
}
vector<int> pre,suf;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u])continue;
int now=rt[v];
if(i)Seg::merge(now,pre[i-1],now);
pre.pb(now);
}
suf.resize(e[u].size());
for(int i=(int)e[u].size()-1;i>=0;i--){
int v=e[u][i];
if(v==fa[u])continue;
int now=rt[v];
if(i+1!=(int)e[u].size())Seg::merge(now,suf[i+1],now);
suf[i]=now;
}
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==fa[u])continue;
int now=rtfa;
if(i)Seg::merge(now,now,pre[i-1]);
if(i+1!=e[u].size())Seg::merge(now,now,suf[i+1]);
dfs2(v,now);
}
}
int main(){
n=read();str=1;
for(int i=2;i<=n;i++){
int u=read();
e[u].pb(i),fa[i]=u,in[u]++,in[i]++;
}
dfs1(str),getrt(str);
dfs2(str,0);
for(int i=1;i<=n;i++)cout<<ans[i]<<'\n';
}