Loading

noip模拟83

A. 树上排列

其实就是考虑尽量去匹配,第一想法就是维护最大最小值,然后考虑会不会有重复.

发现很难做,换个思路,感觉可能会是类似于字符串那样的匹配.

发现很难维护直接匹配,由于题目是让我们判定 \(Yes\)\(No\),所以概率性算法有时候很有用.

本题目中维护一个稍微厉害点的 \(Hash\) 就可以了.

可以维护乘法和加法然后大质数取模,思路来自 \(Yubai\).

可以选择维护 \(sum^x\),思路来自沈老师.

可以选择维护异或 \(Hash\),思路来自题解.

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	// #define int long long 
	#define lf long double
	#define pb push_back
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
		for(;!isdigit(ch);ch=getchar()) cit=(ch=='-');
		for(;isdigit(ch);ch=getchar()) w=(w<<3)+(w<<1)+(ch^48);
		return cit?(-w):w;
	};
} using namespace BSS;

#define ls (x<<1)
#define rs (x<<1|1)

const int N=3e5+21,inf=1e9;

int m,n,ts,ops,cnt;
int tp[N],fa[N],val[N],siz[N],hson[N],dfn[N],rk[N],dep[N],head[N],vis[N];
struct I { int mn,mx; } tr[N<<1];
struct II { int u,v,nxt; } e[N<<1];
auto add=[](int u,int v)->void{
	e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
	head[u]=ts;
};
void sch(int u,int dad){
	fa[u]=dad,dep[u]=dep[dad]+1,siz[u]=1;
	for(int i=head[u],v;i;i=e[i].nxt){
		if((v=e[i].v)==dad) continue;
		sch(v,u),siz[u]+=siz[v];
		if(siz[v]>siz[hson[u]]) hson[u]=v;
	}
}
void dfs(int u){
	rk[dfn[u]=++cnt]=u;
	tp[u]=(hson[fa[u]]==u ? tp[fa[u]] : u);
	if(!hson[u]) return ; dfs(hson[u]);
	for(int i=head[u],v;i;i=e[i].nxt){
		v=e[i].v; 
		if(v==hson[u] or v==fa[u]) continue;
		dfs(v);
	}
}
auto pushup=[](int x)->void{
	tr[x].mn=min(tr[ls].mn,tr[rs].mn);
	tr[x].mx=max(tr[ls].mx,tr[rs].mx);
};
void build(int x,int l,int r){
	tr[x].mn=inf,tr[x].mx=0;
	if(l==r) return tr[x].mn=tr[x].mx=val[rk[l]],void();
	int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r);
	pushup(x);
}
void upd(int x,int l,int r,int px){
	if(l==r) return assert(px==r),tr[x].mn=tr[x].mx=val[rk[l]],void();
	int mid=(l+r)>>1; 
	px<=mid ? upd(ls,l,mid,px) : upd(rs,mid+1,r,px) ;
	pushup(x);
}
int qmax(int x,int l,int r,int ql,int qr){
	if(ql>qr) return 0;
	if(l>=ql and r<=qr) return tr[x].mx;
	int mid=(l+r)>>1,res=0;
	if(ql<=mid) res=max(res,qmax(ls,l,mid,ql,qr));
	if(qr>mid) res=max(res,qmax(rs,mid+1,r,ql,qr));
	pushup(x); return res;
}
int qmin(int x,int l,int r,int ql,int qr){
	if(ql>qr) return inf;
	if(l>=ql and r<=qr) return tr[x].mn;
	int mid=(l+r)>>1,res=inf;
	if(ql<=mid) res=min(res,qmin(ls,l,mid,ql,qr));
	if(qr>mid) res=min(res,qmin(rs,mid+1,r,ql,qr));
	pushup(x); return res;
}
inline int qmax(int x,int y,int &z,int res=0){
	while(tp[x]^tp[y]){
		if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
		res=max(res,qmax(1,1,n,dfn[tp[x]],dfn[x])),x=fa[tp[x]];
	}
	if(dep[x]>dep[y]) swap(x,y); z=x;
	return max(res,qmax(1,1,n,dfn[x],dfn[y]));
}
inline int qmin(int x,int y,int &z,int res=inf){
	while(tp[x]^tp[y]){
		if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
		res=min(res,qmin(1,1,n,dfn[tp[x]],dfn[x])),x=fa[tp[x]];
	}
	if(dep[x]>dep[y]) swap(x,y); z=x;
	return min(res,qmin(1,1,n,dfn[x],dfn[y]));
}
auto check=[](int x,int z)->bool{
	vis[val[x]]++;
	if(vis[val[x]]>1) return 0;
	while(x^z){
		x=fa[x],vis[val[x]]++;
		if(vis[val[x]]>1) return 0;
	}
	return 1;
};
auto PreWork=[]()->void{
	cnt=0,ts=0;
	for(int i=1;i<=n;i++){
		tp[i]=0,fa[i]=0,val[i]=0,siz[i]=0,hson[i]=0,dfn[i]=0,dep[i]=0,head[i]=0,vis[i]=0;
	}
};
auto Work=[]()->void{
	n=read(),ops=read(); int u,v,w,x,y,z,opt;
	PreWork();
	for(int i=1;i<=n;i++) val[i]=read(),fa[i]=0;
	for(int i=2;i<=n;i++) u=read(),v=read(),add(u,v),add(v,u);
	sch(1,0),dfs(1),build(1,1,n);
	// for(int i=1;i<=n;i++) cout<<dfn[i]<<' '<<rk[i]<<endl;
	while(ops--){
		opt=read(),x=read(),y=read();
		if(opt&1){
			if(!(x and y)) { puts("No"); continue; }
			if(qmin(x,y,z)^1) { puts("No"); continue; }
			w=dep[x]+dep[y]-dep[z]*2+1;
			if(qmax(x,y,z)^w) { puts("No"); continue; }
			for(int i=1;i<=w;i++) vis[i]=0;
			if(!check(x,z)) { puts("No"); continue; }
			vis[val[z]]--;
			if(!check(y,z)) { puts("No"); continue; }
			puts("Yes"); 
		}
		else{
			if(x) val[x]=y,upd(1,1,n,dfn[x]);
		}
	}
};
signed main(){
	File(a);
	for(int Ts=read();Ts;Ts--) Work();
	exit(0);
}

B. 连任

很套路的一个题,线段树分治 + 可撤销并查集,板子题,没啥好说的.

B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define int long long 
	#define lf long double
	#define pb push_back
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
		for(;!isdigit(ch);ch=getchar()) cit=(ch=='-');
		for(;isdigit(ch);ch=getchar()) w=(w<<3)+(w<<1)+(ch^48);
		return cit?(-w):w;
	};
} using namespace BSS;

#define ls (x<<1)
#define rs (x<<1|1)
#define merge asasasasas
const int N=1e5+21,mod=1e9+7;

int m,n,ts,cnt,ans;
int fa[N],siz[N],inv[N];
struct I { int u,v; } e[N];
struct II { int x,y,z; } stk[N<<3];
unordered_map<int,int> to[N];
vector<int> vec[N];
vector<I> tr[N<<2];
auto getfa=[](int x)->int{ while(x^fa[x]) x=fa[x]; return x; };
auto merge=[](int x,int y)->void{
	x=getfa(x),y=getfa(y); if(siz[x]>siz[y]) swap(x,y);
	fa[x]=y,siz[y]+=siz[x];
};
void upd(int x,int l,int r,int ql,int qr,I w){
	if(l>=ql and r<=qr) return tr[x].pb(w),void();
	int mid=(l+r)>>1; 
	if(ql<=mid) upd(ls,l,mid,ql,qr,w);
	if(qr>mid) upd(rs,mid+1,r,ql,qr,w);
}
void dfs(int x,int l,int r){
	int u,v,w=cnt,mid=(l+r)>>1;
	for(auto i : tr[x]){
		u=getfa(i.u),v=getfa(i.v);
		if(u==v) continue;
		stk[++cnt]=(II){u,fa[u],siz[u]};
		stk[++cnt]=(II){v,fa[v],siz[v]};
		ans=ans*inv[siz[u]]%mod*inv[siz[v]]%mod*(siz[u]+siz[v])%mod;
		merge(u,v);
	}
	// cout<<"L and R:"<<l<<' '<<r<<"\n";
	// for(int i=1;i<=n;i++) cout<<getfa(i)<<' '; puts("");
	if(l==r) printf("%lld\n",ans);
	else dfs(ls,l,mid),dfs(rs,mid+1,r);
	while(cnt^w){
		u=stk[cnt].x,fa[u]=stk[cnt].y,siz[u]=stk[cnt].z,cnt--;
		v=stk[cnt].x,fa[v]=stk[cnt].y,siz[v]=stk[cnt].z,cnt--;
		ans=ans*inv[siz[u]+siz[v]]%mod*siz[u]%mod*siz[v]%mod;
	}		
}
signed main(){
	File(b);
	n=read(),m=read(),inv[1]=1,ans=1; int opt,u,v,w,x,y,z;
	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++){
		opt=read(),u=read(),v=read(); if(u>v) swap(u,v);
		if(to[u].find(v)!=to[u].end()) vec[to[u][v]].pb(i);
		else to[u][v]=++ts,e[ts]=(I){u,v},vec[ts].pb(i);
	}
	for(int i=1;i<=ts;i++){
		u=e[i].u,v=e[i].v,x=to[u][v];
		if(vec[x].size()&1) vec[x].pb(m+1);
		for(int j=0,lmj=vec[x].size();j<lmj;j+=2){
			y=vec[x][j],z=vec[x][j+1],upd(1,1,m,y,z-1,e[i]);
		}
	}
	dfs(1,1,m);
	exit(0);
}

C. 排列

仍然是最基础的期望线性性拆解,于是发现只有 \(p_i,q_i\) 不确定的东西不太好做.

解决三维偏序可以无脑地进行 \(O(nlogn^2)\)\(CDQ\) 或树套树,当然如果会 \(KD-Tree\) 的话可以写成 \(O(nlogn)\) 的.

但是发现任意两个三元组之间都至少满足一个二维偏序,满足三维偏序的可以满足三个二维偏序.

于是容斥写一下就好了.

C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	// #define int long long 
	#define lf long double
	#define pb push_back
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
		for(;!isdigit(ch);ch=getchar()) cit=(ch=='-');
		for(;isdigit(ch);ch=getchar()) w=(w<<3)+(w<<1)+(ch^48);
		return cit?(-w):w;
	};
} using namespace BSS;

#define LL long long 
const LL mod=998244353;
const int N=1e6+21;

int ans;
int m,n,cnt,tot;
int rk[N],inv[N],vis[N],suf[N],pre[N];
struct I { int a,b,id; } p[N],t[N],tmp[N];
auto inc=[](LL i,int j)->int{ i+=j; return i-(i>=mod)*mod; };
struct FenWick{
	int res,lmx; int c[N<<1];
	inline void upd(int x,int w){
		for(x++;x<=lmx;x+=lbt(x)) c[x]+=w;
	}
	inline void del(int x){
		for(x++;x<=lmx;x+=lbt(x)) c[x]=0;
	}
	inline int qry(int x){
		for(res=0,x++;x;x&=x-1) res+=c[x]; return res;
	}
}A;
signed main(){
	File(c);
	n=read(),inv[1]=1; int u,v,w,x,y,z;
	for(LL i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++) p[i].a=read(),p[i].id=i;
	for(int i=1;i<=n;i++){
		p[i].b=read();
		if(~p[i].b) t[++cnt]=p[i],vis[p[i].b]=1;
	}
	for(int i=1;i<=n;i++) if(!vis[i]) rk[++tot]=i;
	for(int i=1;i<=n;i++) pre[i]=pre[i-1]+(!vis[i]);
	for(int i=n;i;i--) suf[i]=suf[i+1]+(!vis[i]);
	A.lmx=n+1;
	for(int i=1;i<=cnt;i++) ans=inc(ans,A.qry(t[i].a)),A.upd(t[i].a,1);
	for(int i=1;i<=cnt;i++) A.del(t[i].a);
	for(int i=1;i<=cnt;i++) ans=inc(ans,A.qry(t[i].b)),A.upd(t[i].b,1);
	for(int i=1;i<=cnt;i++) A.del(t[i].b);
	sort(t+1,t+1+cnt,[](I i,I j){ return i.a<j.a; });
	for(int i=1;i<=cnt;i++) ans=inc(ans,A.qry(t[i].b)),A.upd(t[i].b,1);
	for(int i=1;i<=cnt;i++) A.del(t[i].b);
	ans=inc(ans,-1ll*cnt*(cnt-1)%mod*inv[2]%mod+mod);
	ans=1ll*ans*inv[2]%mod;
	// if(!tot) printf("%lld\n",ans),exit(0);
	// printf("cnt:%lld ans:%lld\n",cnt,ans);	
	// for(int i=1;i<=n;i++) cout<<pre[i]<<' '; puts("");
	// for(int i=n;i>=1;i--) cout<<suf[i]<<' '; puts("");
	for(int i=1;i<=n;i++){
		x=A.qry(p[i].a);
		if(!~p[i].b) A.upd(p[i].a,1),ans=inc(ans,1ll*inv[2]*x%mod);
		else ans=inc(ans,1ll*pre[p[i].b]*x%mod*inv[tot]%mod);
	}
	// printf("ans:%lld\n",ans);
	for(int i=1;i<=n;i++){
		if(!~p[i].b) A.del(p[i].a);
	}
	for(int i=n;i;i--){
		if(!~p[i].b) A.upd(n-p[i].a+1,1);
		else x=A.qry(n-p[i].a+1),ans=inc(ans,1ll*suf[p[i].b]*x%mod*inv[tot]%mod);
	}
	for(int i=n;i;i--){
		if(!~p[i].b) A.del(n-p[i].a+1);
	}
	printf("%d\n",ans),exit(0);
}

D. 追逐

关于这种双方最优问题( 博弈论问题 ),最终答案都是一定的.

首先观察得到 qjd 和妹子分别的操作大概是长什么样子的.

先把 \(t\) 提根,因为比较方便.

qjd 一定是沿着 \(s\)\(t\) 的链一步步往上走,
往上走的过程中可能会找一个比较优并且自己还能进去的子树钻进去,
然后这个时候由于钻进去之后不能再走已经走过的边,然后这个时候妹子进行各种封杀操作,让 ta 不再进入其它子树.
这个时候妹子再清除标记,把 qjd 释放出来,让 qjd 往上面走.

关于如何定义这个优的子树,优劣是相对于 qjd 而言的,由于 \(qjd\) 想让妹子操作数量多,所以会尽量跑到操作数更多的子树中.
这个东西可以直接树形 \(dp\) 求,\(dp_u=Nxtmax\ dp_v\)

观察这个过程,首先 qjd 最多只会进入一棵子树,因为一旦进入了就会被封杀,这一点游戏双方都已知.
对于任意一个 \(s\)\(t\) 的路径上的点 \(i\),qjd 一定不能进入 \(i\) 的最优子树,因为是妹子先手,这个显然.

所以我们如果答案已知,那么双方的这些操作就都已经确定了:
如果 qjd 进入这棵子树会让妹子的操作次数超过答案,那么就必须要删掉这棵子树.
如果一定要删掉的子树的数目大于了你可以操作的数目,那么妹子的这个答案一定不合法.
由此我们可以选择二分一个妹子的操作数目,然后每次 check.

回过头来看,我们如果不二分直接做贪心,发现很多限制条件都消失了,从而导致很难转移,所以二分提供一个限制还是很有用的.

D_code


#include<bits/stdc++.h>
using namespace std;
namespace BSS{
	#define int long long 
	#define lf long double
	#define pb push_back
	#define mp make_pair
	#define lb lower_bound
	#define ub upper_bound
	#define lbt(x) ((x)&(-(x)))
	#define Fill(x,y) memset(x,y,sizeof(x))
	#define Copy(x,y) memcpy(x,y,sizeof(x))
	#define File(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
	auto read=[](int w=0,bool cit=0,char ch=getchar())->int{
		for(;!isdigit(ch);ch=getchar()) cit=(ch=='-');
		for(;isdigit(ch);ch=getchar()) w=(w<<3)+(w<<1)+(ch^48);
		return cit?(-w):w;
	};
} using namespace BSS;

const int N=3e6+21;

int m,n,s,t,ts;
int fa[N],dp[N],to[N],du[N],pre[N],head[N];
struct I { int u,v,nxt; } e[N<<1];
auto add=[](int u,int v)->void{
	e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u];
	head[u]=ts;
};
void sch(int u,int dad){
	int x=0,y=0; fa[u]=dad;
	for(int i=head[u],v;i;i=e[i].nxt){
		if((v=e[i].v)==dad) continue;
		sch(v,u),du[u]++;
		if(dp[v]>x) y=x,x=dp[v];
		else if(dp[v]>y) y=dp[v];
	}
	dp[u]=y+du[u];
}
auto check=[](int x)->bool{
	// cout<<"x:"<<x<<endl;
	// cout<<"u:"<<u<<" ops:"<<dp[u]<<' '<<fa[u]<<' '<<pre[fa[u]]<<' '<<dp[u]+pre[fa[u]]+ns<<endl;
	// cout<<"ns:"<<ns<<endl;	
	for(int u=s,now=1,ns=0,tmp;u^t;now++,u=fa[u]){
		tmp=0;
		for(int i=head[u],v;i;i=e[i].nxt){
			if(v=e[i].v,v==to[u] or v==fa[u]) continue;
			if(dp[v]+ns+pre[u]>x) tmp++;
		}
		ns+=tmp;
		if(ns>now or ns>x) return 0;
	}
	return 1;
};
signed main(){
	File(d);
	n=read(),t=read(),s=read(); int u,v;
	for(int i=2;i<=n;i++){
		u=read(),v=read(),add(u,v),add(v,u);
	}
	if(s==t) puts("0"),exit(0);
	sch(t,0),u=s;
	while(u^t) to[fa[u]]=u,u=fa[u];
	u=to[t];
	while(u^s) pre[u]=pre[fa[u]]+du[u]-1,u=to[u];
	pre[u]=pre[fa[u]]+du[u];
	// for(int i=1;i<=n;i++) cout<<pre[i]<<' '; puts("");
	// for(int i=1;i<=n;i++) cout<<dp[i]<<' '; puts("");
	int l=0,r=n*n,mid,res=r;
	while(l<=r){
		mid=(l+r)>>1; 
		check(mid) ? (r=mid-1,res=mid) : (l=mid+1) ;
	}
	printf("%lld\n",res),exit(0); 
}
posted @ 2021-11-12 21:42  AaMuXiiiiii  阅读(41)  评论(0编辑  收藏  举报