2021.11.12考试总结[NOIP模拟83]
T1 树上排列
考场上一直以为要转换什么限制,结果就是哈希一下。。(不过确实没见过这个套路
找个与顺序无关的哈希,树剖维护一下就行了。(我写的平方加和好像是假的
\(code:\)
T1
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define int LL
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0'||v>'9')f|=(v=='-'),v=gc();
while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=250010;
int t,n,q,op,u,v,a[NN],perm[NN];
int idx,head[NN];
bool vis[NN];
struct edge{ int to,nex; }e[NN<<1];
void add(int a,int b){
e[++idx]=(edge){b,head[a]}; head[a]=idx;
e[++idx]=(edge){a,head[b]}; head[b]=idx;
}
namespace Tree_Chain{
int cnt,id[NN],fa[NN],dfn[NN],dep[NN],top[NN],siz[NN],son[NN];
void dfs1(int s,int f){
fa[s]=f; siz[s]=1; dep[s]=dep[f]+1;
for(int v,i=head[s];i;i=e[i].nex) if((v=e[i].to)!=f){
dfs1(v,s);
siz[s]+=siz[v];
if(siz[v]>siz[son[s]]) son[s]=v;
}
}
void dfs2(int s,int t){
dfn[s]=++cnt; top[s]=t; id[cnt]=s;
if(!son[s]) return;
dfs2(son[s],t);
for(int v,i=head[s];i;i=e[i].nex)
if((v=e[i].to)!=fa[s]&&e[i].to!=son[s]) dfs2(v,v);
}
int LCA(int x,int y){
while(top[x]!=top[y])
dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
return dep[x]<dep[y]?x:y;
}
namespace Segment_Tree{
#define ld rt<<1
#define rd (rt<<1)|1
int sm[NN<<2];
void pushup(int rt){ sm[rt]=sm[ld]+sm[rd]; }
void build(int rt,int l,int r){
if(l==r) return sm[rt]=a[id[l]]*a[id[l]],void();
int mid=l+r>>1;
build(ld,l,mid); build(rd,mid+1,r);
pushup(rt);
}
void modify(int pos,int val,int rt=1,int l=1,int r=n){
if(l==r) return sm[rt]=val,void();
int mid=l+r>>1;
if(pos<=mid) modify(pos,val,ld,l,mid);
else modify(pos,val,rd,mid+1,r);
pushup(rt);
}
int query(int opl,int opr,int rt=1,int l=1,int r=n){
if(l>=opl&&r<=opr) return sm[rt];
int mid=l+r>>1,res=0;
if(opl<=mid) res+=query(opl,opr,ld,l,mid);
if(opr>mid) res+=query(opl,opr,rd,mid+1,r);
return res;
}
} using namespace Segment_Tree;
int ASK(int x,int y){
int res=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res+=query(dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return res+query(dfn[x],dfn[y]);
}
} using namespace Tree_Chain;
signed main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
t=read();
for(int i=1;i<=250000;i++) perm[i]=perm[i-1]+i*i;
while(t--){
idx=cnt=0;
memset(head,0,sizeof(head));
memset(son,0,sizeof(son));
n=read(); q=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int a,b,i=1;i<n;i++)
a=read(),b=read(),add(a,b);
dfs1(1,0); dfs2(1,1); build(1,1,n);
while(q--){
op=read(); u=read(); v=read();
if(op==1){
if(!u||!v){ puts("No"); continue; }
int len=dep[u]+dep[v]-2*dep[LCA(u,v)]+1;
if(ASK(u,v)==perm[len]) puts("Yes");
else puts("No");
} else modify(dfn[u],v*v);
}
}
return 0;
}
T2 连任
可撤销并查集加线段树分治,但这俩都不会。。
并查集要撤销就不能路径压缩,只启发式合并。每次合并时把操作压进栈内,便于之后撤销。
在时间轴上建立线段树,把每条边存在的时段插进线段树内,之后在线段树上 \(DFS\) ,进入节点时将边的端点合并,回溯时撤销。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
#define int LL
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
typedef pair<int,int> PII;
#define mpr make_pair
#define fi first
#define se second
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0'||v>'9')f|=(v=='-'),v=gc();
while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=100010,mod=1e9+7;
int n,m,u,v,op,tot,now,inv[NN],ans[NN];
struct node{ int u,v,l,r;}opr[NN];
map<int,int>tim[NN];
namespace DSU{
int top,fa[NN],siz[NN];
PII stk[NN];
int getf(int x){ return fa[x]==x?x:getf(fa[x]); }
void merge(PII s){
int x=getf(s.fi),y=getf(s.se);
if(x==y) return stk[++top]=mpr(-1,-1),void();
now=now*inv[siz[x]]%mod*inv[siz[y]]%mod*(siz[x]+siz[y])%mod;
if(siz[x]>siz[y]) swap(x,y);
fa[x]=y; siz[y]+=siz[x];
stk[++top]=mpr(x,y);
}
void delet(){
int x=stk[top].fi,y=stk[top].se; --top;
if(x==-1) return;
now=now*inv[siz[y]]%mod*siz[x]%mod*(siz[y]-siz[x])%mod;
fa[x]=x; siz[y]-=siz[x];
}
} using namespace DSU;
namespace Devide_on_Segment_Tree{
#define ld rt<<1
#define rd (rt<<1)|1
vector<PII>add[NN<<2];
void modify(int opl,int opr,PII val,int rt=1,int l=1,int r=m){
if(l>=opl&&r<=opr)
return add[rt].push_back(val),void();
int mid=l+r>>1;
if(opl<=mid) modify(opl,opr,val,ld,l,mid);
if(opr>mid) modify(opl,opr,val,rd,mid+1,r);
}
void solve(int rt,int l,int r){
for(auto v:add[rt]) merge(v);
if(l==r) ans[l]=now;
else{
int mid=l+r>>1;
solve(ld,l,mid);
solve(rd,mid+1,r);
}
for(auto v:add[rt]) delet();
}
} using namespace Devide_on_Segment_Tree;
signed main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
n=read(); m=read(); now=1;
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=mod-(mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=m;i++){
op=read(); u=read(); v=read();
if(op==1){
tim[u][v]=tim[v][u]=++tot;
opr[tot]=(node){u,v,i,0};
} else opr[tim[u][v]].r=i-1;
}
for(int i=1;i<=tot;i++){
if(!opr[i].r) opr[i].r=m;
modify(opr[i].l,opr[i].r,mpr(opr[i].u,opr[i].v));
}
solve(1,1,m);
for(int i=1;i<=m;i++) write(ans[i],'\n');
return 0;
}
T3 排列
三维偏序竟能单log求。。
实际上考场上也就差这一步了。考虑现有三个排列 \(o,p,q\) ,要求它们的三维偏序对数。
我们对 \((o,p),(p,q),(o,q)\) 分别做二维偏序,对于一对下标 \(i,j\) ,会有三个偏序 \((o_i,o_j),(p_i,p_j),(q_i,q_j)\) ,但偏序只有大于小于两种关系,因此这一对下标要么恰好存在两个数字满足偏序关系,要么满足三维偏序。前者在三个二维偏序中会被算 \(1\) 遍,后者会被算 \(3\) 遍。容斥一下,令三个二维偏序对数和为 \(M\) ,则总三维偏序对数 \(N\) 有
\[N=\frac{M-\frac{n(n-1)}{2}}{2}
\]
对于 \(q\neq-1\) 的情况,答案可以用这种方法算出。考虑 \(q=-1\) 的情况,算出这个 \(q\) 取值大于任意数 \(x\) 的概率 \(prob_x\) ,分别考虑它之前不为 \(-1\) 的数,它之后不为 \(-1\) 的数和除它之外的 \(-1\) 产生贡献的期望,加和即可。具体实现就是二维偏序。
\(code:\)
T3
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0'||v>'9')f|=(v=='-'),v=gc();
while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010,mod=998244353,inv2=499122177;
int n,ans,p[NN],q[NN],prob[NN];
bool vis[NN];
vector<int>pos,num;
void add(int& a,int b){
a+=b;
if(a<0) a+=mod;
if(a>=mod) a-=mod;
}
int qpow(int a,int b,int res=1){
for(;b;b>>=1,a=1ll*a*a%mod)
if(b&1) res=1ll*res*a%mod;
return res;
}
namespace BIT{
int c[NN];
void insert(int pos,int val){
while(pos<=n)
add(c[pos],val), pos+=pos&-pos;
}
int query(int pos,int res=0){
while(pos)
add(res,c[pos]), pos-=pos&-pos;
return res;
}
} using namespace BIT;
struct node{
int x,y;
bool operator<(const node& a){
return x<a.x;
}
}nd[NN];
int cnt;
void solve(){
for(int i=1;i<=n;i++) if(q[i]>0){
insert(p[i],1);
add(ans,query(p[i]-1));
}
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) if(q[i]>0){
insert(q[i],1);
add(ans,query(q[i]-1));
}
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) if(q[i]>0)
nd[++cnt]=(node){p[i],q[i]};
sort(nd+1,nd+cnt+1);
for(int i=1;i<=cnt;i++){
insert(nd[i].y,1);
add(ans,query(nd[i].y-1));
}
ans=1ll*(ans-1ll*cnt*(cnt-1)/2%mod+mod)*inv2%mod;
}
signed main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) p[i]=read();
for(int i=1;i<=n;i++){
q[i]=read();
if(q[i]>0) vis[q[i]]=1;
}
for(int i=1;i<=n;i++)
if(!vis[i]) num.push_back(i);
solve();
if(num.empty()) return write(ans,'\n'),0;
int tmp=qpow(num.size(),mod-2);
for(int i=0;i<num.size();i++)
prob[num[i]-1]=tmp;
for(int i=n;i;i--) add(prob[i],prob[i+1]);
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
if(q[i]>0) insert(p[i],prob[q[i]]);
else add(ans,query(p[i]-1));
memset(c,0,sizeof(c));
for(int i=n;i;i--)
if(q[i]>0) insert(p[i],mod+1-prob[q[i]]);
else add(ans,query(n)-query(p[i]));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) if(q[i]<0){
insert(p[i],inv2);
add(ans,query(p[i]-1));
}
write(ans,'\n');
return 0;
}
T4 追逐

属实给我调吐了
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL; typedef long double LD;
typedef unsigned long long ULL; typedef double DB;
#define freopen FL=freopen
FILE *FL;
const int Mxdt=100000;
static char buf[Mxdt],Ch[50],*p1=buf,*p2=buf;
inline char gc(){ return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++; }
inline int read(){
int t=0,f=0;char v=gc();
while(v<'0'||v>'9')f|=(v=='-'),v=gc();
while(v>='0'&&v<='9')t=(t<<3)+(t<<1)+v-48,v=gc();
return f?-t:t;
}
void write(int x,char sp){
int len=0;
if(x<0) x=-x,putchar('-');
do{ Ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(Ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010;
int n,t,s,res,sum,fa[NN];
int f[NN];
bool on[NN];
vector<int>e[NN];
void add(int a,int b){
e[a].push_back(b);
e[b].push_back(a);
}
void dfs(int u,int ff){
int cnt=0,mx1=0,mx2=0; fa[u]=ff;
for(int v:e[u]) if(v!=ff){
++cnt; dfs(v,u);
int tmp=f[v];
if(tmp>mx1) swap(mx1,tmp);
if(tmp>mx2) swap(mx2,tmp);
}
f[u]=mx2+cnt;
}
void init(){
int u=s,pre=0;
while(u!=t){
for(int v:e[u]) sum+=(v!=fa[u]&&v!=pre);
pre=u; u=fa[u];
}
}
bool check(int mid){
int u=s,pre=0,all=sum,del=0,now=1;
while(u!=t){
int tmp=0,pmt=0;
for(int v:e[u]) if(v!=pre&&v!=fa[u]){
if(f[v]+all+del>mid) ++tmp;
++pmt;
}
all-=pmt; del+=tmp;
if(del>now||del>mid) return 0;
pre=u; u=fa[u]; ++now;
}
return 1;
}
void search(){
int l=0,r=1000;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) r=mid-1,res=mid;
else l=mid+1;
}
}
signed main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
n=read(); t=read(); s=read();
for(int a,b,i=1;i<n;i++)
a=read(),b=read(),add(a,b);
dfs(t,0); init(); search();
write(res,'\n');
return 0;
}

浙公网安备 33010602011771号