Noip模拟96 2021.11.12


T1 树上排列

比较水的一道题,不过考场上死活没有想出来使用个区间连乘判断哈希,然后就\(gg\)

我的应该不容易被卡,因为有四重保证,要卡的话数据必须够早的非常精心,不过我还没有想到卡他的方法

但是同样的,正确性高了代码长度和运行时间都是消耗非常大的。。。。

不建议看我的代码,去看别人的哈希可能打的更快,但是我的基本没哈希冲突

a
#include
#define int long long
using namespace std;
namespace AE86{
	FILE *wsn;
	#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
	char buf[1<<20],*p1=buf,*p2=buf;
	auto read=[](){
		int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;

const int NN=250005,inf=1e9,mod=998244353;
int T,n,q,w[NN],h[NN];
struct SNOW{int to,next;}e[NN<<1];int head[NN],rp;
auto add=[](int x,int y){
	e[++rp]=SNOW{y,head[x]};head[x]=rp;
	e[++rp]=SNOW{x,head[y]};head[y]=rp;
};
int dfn[NN],rk[NN],dep[NN],son[NN],top[NN],siz[NN],fa[NN],cnt;
inline void dfs1(int f,int x){
	dep[x]=dep[f]+1; siz[x]=1; fa[x]=f;
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].to; if(y==f) continue;
		dfs1(x,y); siz[x]+=siz[y];
		if(siz[son[x]]dfn[y])swap(x,y);
	return x;
}
struct SNOWtree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	int ll[NN<<2],rr[NN<<2],mn[NN<<2],sm[NN<<2],mx[NN<<2],ml[NN<<2];
	inline void pushup(int id){
		if(ll[id]==rr[id]) return;
		mn[id]=min(mn[lid],mn[rid]);
		mx[id]=max(mx[lid],mx[rid]);
		sm[id]=sm[lid]+sm[rid];
		ml[id]=ml[lid]*ml[rid]%mod;
	}
	inline void build(int id,int l,int r){
		ll[id]=l;rr[id]=r;mn[id]=inf;mx[id]=0;sm[id]=0;ml[id]=1;
		if(l==r)return mn[id]=sm[id]=mx[id]=ml[id]=w[rk[l]],void();
		int mid=l+r>>1;build(lid,l,mid);build(rid,mid+1,r);pushup(id);
	}
	inline void update(int id,int pos,int v){
		if(ll[id]==rr[id]) return mn[id]=mx[id]=ml[id]=sm[id]=v,void();int mid=ll[id]+rr[id]>>1;
		if(pos<=mid) update(lid,pos,v);else update(rid,pos,v);pushup(id);
	}
	inline int query1(int id,int l,int r){
		if(l<=ll[id]&&rr[id]<=r) return sm[id];int mid=ll[id]+rr[id]>>1,ans=0;
		if(l<=mid) ans+=query1(lid,l,r);if(r>mid) ans+=query1(rid,l,r);
		return ans;
	}
	inline int query2(int id,int l,int r){
		if(l<=ll[id]&&rr[id]<=r) return mx[id];int mid=ll[id]+rr[id]>>1,ans=0;
		if(l<=mid) ans=max(ans,query2(lid,l,r));if(r>mid) ans=max(ans,query2(rid,l,r));
		return ans;
	}
	inline int query3(int id,int l,int r){
		if(l<=ll[id]&&rr[id]<=r) return mn[id];int mid=ll[id]+rr[id]>>1,ans=inf;
		if(l<=mid) ans=min(ans,query3(lid,l,r));if(r>mid) ans=min(ans,query3(rid,l,r));
		return ans;
	}
	inline int query4(int id,int l,int r){
		if(l<=ll[id]&&rr[id]<=r) return ml[id];int mid=ll[id]+rr[id]>>1,ans=1;
		if(l<=mid) ans=ans*query4(lid,l,r)%mod;if(r>mid) ans=ans*query4(rid,l,r)%mod;
		return ans;
	}
}tr;
inline int SUM(int x,int y,int ans=0){
	while(top[x]!=top[y]){
		if(dep[top[x]]dfn[y]) swap(x,y);
	ans+=tr.query1(1,dfn[x],dfn[y]);
	return ans;
}
inline int MAX(int x,int y,int ans=0){
	while(top[x]!=top[y]){
		if(dep[top[x]]dfn[y]) swap(x,y);
	ans=max(ans,tr.query2(1,dfn[x],dfn[y]));
	return ans;
}
inline int MIN(int x,int y,int ans=inf){
	while(top[x]!=top[y]){
		if(dep[top[x]]dfn[y]) swap(x,y);
	ans=min(ans,tr.query3(1,dfn[x],dfn[y]));
	return ans;
}
inline int MUL(int x,int y,int ans=1){
	while(top[x]!=top[y]){
		if(dep[top[x]]dfn[y]) swap(x,y);
	ans=ans*tr.query4(1,dfn[x],dfn[y])%mod;
	return ans;
}
int opt,x,y;
inline int calc(int n){return n*(n+1)/2;}
auto clear=[](){
	memset(head,0,sizeof(head)); rp=0;
	memset(son,0,sizeof(son)); cnt=0;
};
namespace WSN{
	inline short main(){
		wsn=freopen("a.in","r",stdin);
		wsn=freopen("a.out","w",stdout);
		T=read(); h[0]=1;
		for(int i=1;i

T2 连任

线段树分治,没有听说过,于是今天刚学会,模板链接

知道了线段树分治的模板及其思想后我们来看这道题,

不难发现可以把所有的操作进行处理,这样就变成了那个模板题,这样我们把所有的冰茶姬合并操作都挂在线段树上,开一个\(vector\)记录下来

然后进行线段树\(dfs\),过程中维护一下联通块大小,并使用其进行按秩合并,这样保证了复杂度是\(O(nlogm)\)

剩下的就和那一道模板题一样在删边的时候进行冰茶姬撤销即可

b
#include
#define int long long
using namespace std;
namespace AE86{
	FILE *wsn;
	#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
	char buf[1<<20],*p1=buf,*p2=buf;
	auto read=[](){
		int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;

const int NN=1e5+5,mod=1e9+7;
typedef pair PII;
#define fi first
#define se second
#define mp make_pair
int n,m,inv[NN],ans=1;
struct node{int x,y;}e[NN];
namespace DSU{
	int fa[NN],siz[NN],top;
	struct Stack{int x,y,szx,szy,tot;}stk[NN];
	inline int getfa(int x){return (fa[x]==x)?x:getfa(fa[x]);}
	inline void merge(int x,int y){
		x=getfa(x); y=getfa(y);
		if(x==y) return;
		if(siz[x]>siz[y]) swap(x,y);
		stk[++top]=(Stack){x,y,siz[x],siz[y],siz[x]+siz[y]};
		ans=ans*inv[siz[x]]%mod*inv[siz[y]]%mod;
		ans=ans*(siz[x]+siz[y])%mod;
		fa[x]=y; siz[y]+=siz[x];
	}
	inline void back(int lst){
		while(top>lst){
			Stack now=stk[top--];
			ans=ans*inv[now.tot]%mod;
			ans=ans*now.szx%mod*now.szy%mod;
			fa[now.x]=now.x; siz[now.y]-=now.szx;
		}
	}
}using namespace DSU;
struct SNOWtree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	#define mid ((l+r)>>1)
	vector tag[NN<<2];
	inline void insert(int id,int l,int r,int L,int R,int v){
		if(l>R||rhas;
struct update{int l,r,i;};vector upd;
namespace WSN{
	inline short main(){
		wsn=freopen("b.in","r",stdin);
		wsn=freopen("b.out","w",stdout);
		n=read();m=read(); inv[0]=inv[1]=1;
		for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
		for(int i=2;i<=n;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for(int i=1;i<=m;i++){
			int opt=read(); e[i].x=read(),e[i].y=read();
			if(e[i].x>e[i].y) swap(e[i].x,e[i].y);
			if(opt&1) has[mp(e[i].x,e[i].y)]=i;
			else{
				upd.push_back(update{has[mp(e[i].x,e[i].y)],i-1,has[mp(e[i].x,e[i].y)]});
				has.erase(has.find(mp(e[i].x,e[i].y)));
			}
		}
		for(auto x:has)upd.push_back(update{x.se,m,x.se});
		for(auto x:upd)tr.insert(1,1,m,x.l,x.r,x.i);
		tr.dfs(1,1,m);
		return 0;
	}
}
signed main(){return WSN::main();}

T3 排列

非常妙的一道二维偏序问题

这题看上去不难想到在\(\forall i,p_i!=-1\)的时候使用\(CDQ\)分治,但是这样复杂度是\(O(nlog^2n)\)只能拿到\(20pts\)

考虑一下如何在\(O(nlogn)\)复杂度内解决这个子任务

为了说明方便,我们设\(a,b,c\)三个数组分别表示下标排列输入的\(p\)输入的\(q\)

做法很简单,只要使用二维偏序分别处理出\((a,b),(b,c),(a,c)\)的偏序对数之和\(M\),那么答案就是\(\frac{1}{2}(M-C_{n}^{2})\)

考虑一对数对\((i,j)\),要么恰好存在两个数字是偏序关系,要么三个数字都是偏序关系,如果是前者,会恰好计算一次,否则会被统计三次,所以答案就是如上的式子

这样的基础上我们在考虑有\(-1\)的情况,我们定义\(ds\)数组为在\(q\)中没有出现的数字的集合

我们只需要预处理出一个数组\(P[i][0/1]\)分别表示\(ds\)集合中比\(i\)小的数的个数,比\(i\)大的个数除以集合总个数,不难发现这个东西就是\(-1\)变成比\(i\)小、大的数的概率

然后考虑进行二维偏序,不妨拿\((b,c)\)来举例

考虑所有可能的数对\((i,j)\),在计算第二维偏序的时候只可能出现四种情况

  1. i=-1,j=k
  2. i=k,j=-1
  3. i=-1,j=-1
  4. i=k,j=kk

先把二元组数组按照\(b\)为第一关键字排序,然后扫的时候记录两个变量\(tmp,big\)分别表示前面的\(-1\)的个数,前面的不是\(-1\)的数比\(-1\)变出来的数小的总期望值,假设当前扫到二元组下标为\(i\)

那么分别考虑其是\(k\)或者\(-1\)的情况

如果是\(k\),那么他前面的所有\(-1\)对其做出的贡献就是\(P[k][0]\times tmp\),不是\(-1\)的直接在树状数组上查询

如果是\(-1\),那么他前面的所有的\(-1\)对其做出的贡献是\(\frac{1}{2}\times tmp\),不是\(-1\)的数做出的总贡献就是\(big\)

c
#include
#define int long long
using namespace std;
namespace AE86{
	FILE *wsn;
	#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
	char buf[1<<20],*p1=buf,*p2=buf;
	auto read=[](){
		int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
	};
	auto write=[](int x,char opt='\n'){
		char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
		do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
		for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
	};
}using namespace AE86;
const int NN=1e6+5,mod=998244353;
inline int qmo(int a,int b,int ans=1){for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
int n,a[NN],b[NN],c[NN],tmp,v2;
int M,tt,ans,no[NN],cnt,P[NN][2];
bool vis[NN];
struct BIT{
	int c[NN];
	inline void update(int x,int v){while(x0){
			big+=P[p[i].y][1];
			ans+=tr.query(p[i].y-1)+P[p[i].y][0]*tmp%mod; ans%=mod;
			tr.update(p[i].y,1);
		}else{
			ans+=big+tmp*v2%mod; ans%=mod;
			++tmp;
		}
	}
	return ans;
}
namespace WSN{
	inline short main(){
		wsn=freopen("c.in","r",stdin);
		wsn=freopen("c.out","w",stdout);
		n=read(); v2=qmo(2,mod-2);
		for(int i=1;i<=n;++i)b[i]=read(),a[i]=i;
		for(int i=1;i<=n;++i){
			c[i]=read();
			if(c[i]>0)vis[c[i]]=1;
		}for(int i=1;i<=n;++i)if(!vis[i])no[++cnt]=i;
		tmp=qmo(cnt,mod-2);
		for(int i=1;i<=n;++i){
			P[i][0]=(lower_bound(no+1,no+cnt+1,i)-no-1)*tmp%mod;
			P[i][1]=(cnt-(upper_bound(no+1,no+cnt+1,i)-no)+1)*tmp%mod;
		}
		int tmp1=calc(a,b),tmp2=calc(a,c),tmp3=calc(b,c);
		M=tmp1+tmp2+tmp3;
		tt=n*(n-1)%mod*v2%mod;
		ans=(M-tt+mod)%mod*v2%mod;
		write(ans);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 追逐

又一个人赢在和他的妹子玩耍了。。。