noip模拟36[我觉得我应该是太困了]

noip模拟36 solutions

我真的觉得我是太困了,啊,只有35pts,我太牛逼了

考试的时候要集中精神,不能想不到正解就自爆自弃了

T1 Dove 打扑克

这个题吧,我考场上是用链表做的,

这个链表不是要维护前趋后继么,我老是忘记,然后考完加上了一句话就65了

具体就是我用并查集和链表统计当前的序列中还剩下几堆

然后直接暴力统计所有的变化,得到答案,再加上一个线段树快速T掉

65pts
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
int n,m,id[N];
int siz[N],fro[N],nxt[N],head;
struct seg_tree{
	#define ls x<<1
	#define rs x<<1|1
	ll sum[N*4];
	void pushup(int x){
		sum[x]=sum[ls]+sum[rs];
		return ;
	}
	void ins(int x,int l,int r,int pos,ll v){
		if(l==r){
			sum[x]+=v;
			return ;
		}
		int mid=l+r>>1;
		if(pos<=mid)ins(ls,l,mid,pos,v);
		else ins(rs,mid+1,r,pos,v);
		pushup(x);return ;
	}
	ll query(int x,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return sum[x];
		int mid=l+r>>1;ll ret=0;
		if(ql<=mid)ret+=query(ls,l,mid,ql,qr);
		if(qr>mid)ret+=query(rs,mid+1,r,ql,qr);
		return ret;
	}
	#undef ls
	#undef rs
}xds;
struct BCJ{
	int fa[N];
	BCJ(){for(re i=1;i<=100000;i++)fa[i]=i;}
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
}bcj;
signed main(){
	scanf("%d%d",&n,&m);	
	head=1;
	for(re i=1;i<=n;i++){
		id[i]=i;
		fro[i]=i-1;
		nxt[i]=i+1;
		siz[i]=1;
	}
	nxt[n]=0;
	xds.ins(1,0,n,0,1ll*n*(n-1)/2);
	while(m--){
		int typ;scanf("%d",&typ);
		if(typ==1){
			int x,y;scanf("%d%d",&x,&y);
			x=bcj.find(x);y=bcj.find(y);
			//cout<<x<<" "<<y<<" "<<id[x]<<" "<<id[y]<<endl;
			if(id[x]==id[y])continue;
			for(re i=head;i;i=nxt[i]){
				if(i==id[x])continue;
				xds.ins(1,0,n,abs(siz[id[x]]-siz[i]),-1);
			}
			if(fro[id[x]])nxt[fro[id[x]]]=nxt[id[x]],fro[nxt[id[x]]]=fro[id[x]];
			else head=nxt[id[x]],fro[nxt[id[x]]]=0;
			for(re i=head;i;i=nxt[i]){
				if(i==id[y])continue;
				xds.ins(1,0,n,abs(siz[id[y]]-siz[i]),-1);
			}
			siz[id[y]]+=siz[id[x]];bcj.fa[x]=y;
			//cout<<siz[id[y]]<<endl;
			for(re i=head;i;i=nxt[i]){
				if(i==id[y])continue;
				xds.ins(1,0,n,abs(siz[id[y]]-siz[i]),1);
			}
		}
		else{
			int c;scanf("%d",&c);
			printf("%lld\n",xds.query(1,0,n,c,n));
		}
	}
}

其实吧,你会发现所有块的取值最多只会有\(\sqrt{n}\)种,所以我们就暴力统计

用一个数组存每一个块的大小,再来一个存当前大小有多少个

直接双指针操作,统计答案就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
int n,m;
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
struct node{
	int sz,va;
	node(){}
	node(int x,int y){
		sz=x;va=y;
	}
};
queue<node> num,bin;
node ask[N];int as=0;
int siz[N];
signed main(){
	scanf("%d%d",&n,&m);
	num.push(node(n,1));
	for(re i=1;i<=n;i++)fa[i]=i,siz[i]=1;
	while(m--){
		int typ;scanf("%d",&typ);
		if(typ==1){
			int x,y;scanf("%d%d",&x,&y);
			int fx=find(x),fy=find(y);
			if(fx==fy)continue;
			int tmp=siz[fx]+siz[fy];bool flag=false;
			while(!num.empty()){
				int sz=num.front().sz,va=num.front().va;num.pop();
				if(va==siz[fx])sz--;
				if(va==siz[fy])sz--;
				if(!sz)continue;
				if(va==tmp&&!flag)sz++,flag=true;
				if(va>tmp&&!flag)bin.push(node(1,tmp)),flag=true;
				//cout<<sz<<endl;
				bin.push(node(sz,va));
			}
			if(!flag)bin.push(node(1,tmp));
			swap(bin,num);
			fa[fx]=fy;siz[fy]+=siz[fx];
		}
		else{
			int c=0;scanf("%d",&c);as=0;
			ll ans=0;
			while(!num.empty()){
				int sz=num.front().sz,va=num.front().va;
				ask[++as]=node(sz,va);
				num.pop();bin.push(node(sz,va));
			}
			swap(num,bin);
			if(c==0){
				for(re i=1;i<=as;i++)ans+=1ll*ask[i].sz*(ask[i].sz-1)/2;
				c++;
			}
			//cout<<num.front().va<<endl;
			int now=0,big=0;
			//cout<<as<<" sb "<<endl;
			for(re i=1;i<=as;i++){
				if(ask[as].va-ask[i].va<c){
					now=i-1;break;
				}
				big+=ask[i].sz;
			}
			//if(!now)now=as-1;
			//cout<<now<<endl;
			for(re i=as;i>=1;i--){
				while(ask[i].va-ask[now].va<c&&now)big-=ask[now].sz,now--;
				if(!now)break;
				//if(now==i)ask[
				ans+=1ll*big*ask[i].sz;
			}
			printf("%lld\n",ans);
		}
	}
}

T2 Cicada 与排序

一般我看到题面上提到了某些算法,我一般直接就放弃这个算法了

没想到啊没想到,这个出题人竟然不按套路出牌,竟然直接让我在归并排序中转移

我们可以根据原来的序列关系搞到一个归并排序的指针移动的概率

然后通过概率搞到转移过去的概率

其实就是直接模拟归并排序的整个过程,相等的就乘上2的逆元

设f[dep][i][j]表示在第dep层从i转移到j的概率,那么我们就可以直接从下一层转移了

最后直接用这个概率得到期望

其他的都和归并排序中的是一样的

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=505;
const ll mod=998244353;
int n,a[N];
ll f[N][N][N],g[N][N],v2=499122177;
void merge(int dep,int l,int r){
	if(l==r){f[dep][l][r]=1;return ;}
	int mid=l+r>>1;merge(dep+1,l,mid);merge(dep+1,mid+1,r);
	memset(g,0,sizeof(g));g[0][0]=1;
	for(re i=0;i<=mid-l+1;i++)
		for(re j=0;j<=r-mid;j++){
			//if(i==mid-l+1&&j==r-mid)continue;
			if(i==mid-l+1)g[i][j+1]=(g[i][j+1]+g[i][j])%mod;
			else if(j==r-mid)g[i+1][j]=(g[i+1][j]+g[i][j])%mod;
			else if(a[i+l]<a[j+mid+1])g[i+1][j]=(g[i+1][j]+g[i][j])%mod;
			else if(a[i+l]>a[j+mid+1])g[i][j+1]=(g[i][j+1]+g[i][j])%mod;
			else{
				g[i+1][j]=(g[i+1][j]+g[i][j]*v2%mod)%mod;
				g[i][j+1]=(g[i][j+1]+g[i][j]*v2%mod)%mod;
			}
		}
	for(re i=l;i<=r;i++)
		for(re j=0;j<=mid-l+1;j++)
			for(re k=0;k<=r-mid;k++){
				if(j==mid-l+1&&k==r-mid)continue;
				if(j==mid-l+1)f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][k+mid+1]*g[j][k]%mod)%mod;
				else if(k==r-mid)f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][j+l]*g[j][k]%mod)%mod;
				else if(a[l+j]<a[mid+k+1])f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][j+l]*g[j][k]%mod)%mod;
				else if(a[l+j]>a[mid+k+1])f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][k+mid+1]*g[j][k]%mod)%mod;
				else{
					f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][j+l]*g[j][k]%mod*v2%mod)%mod;
					f[dep][i][j+k+l]=(f[dep][i][j+k+l]+f[dep+1][i][k+mid+1]*g[j][k]%mod*v2%mod)%mod;
				}
			}
	sort(a+l,a+r+1);
}
signed main(){
	scanf("%d",&n);
	for(re i=1;i<=n;i++)scanf("%d",&a[i]);
	merge(1,1,n);
	for(re i=1;i<=n;i++){
		ll ans=0;
		for(re j=1;j<=n;j++)
			ans=(ans+f[1][i][j]*j%mod)%mod;
		printf("%lld ",ans);
	}
}

T3 Cicada 拿衣服

这小衣服拿的不太合常理啊。。。。。。。。

哦我想起来了,我这个题好像是测试点分治来着。。。。。

我只是简单的证了一下,确实是有单调性的

对于那种or-and最多只有2logn次变化的,每一位嘛

min-max一定是递减的,随着区间范围的扩大

我们对于每一个右端点都去统计从左端点开始枚举的log个or-and的区间

我们在这个区间内二分,用st表直接统计,这样的话复杂度就是\(\mathcal{O(nlog^2n)}\)

所以下面粘上我的分治代码,注意n>40000时break是重点

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e6+5;
int n;
ll a[N];
ll jk;
int ans[N];
signed main(){
	scanf("%d%lld",&n,&jk);
	for(re i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(re i=1;i<=n;i++){
		int mr=-1,l=i;
		ll rn=0x3f3f3f3f3f3f3f3f,rx=0,ro=0,ra=(1ll<<40)-1,tmp=0;
		for(re r=i;r<=n;r++){
			rn=min(rn,a[r]);
			rx=max(rx,a[r]);
			ro|=a[r];
			ra&=a[r];
			tmp=rn+ro-rx-ra;
			if(n>40000&&r-i+1>700)break;
			if(tmp>=jk)mr=r;
		}
		if(mr==-1){
			if(!ans[i])ans[i]=-1;
			continue;
		}
		for(re j=i;j<=mr;j++)
			ans[j]=max(ans[j],mr-l+1);
	}
	for(re i=1;i<=n;i++)printf("%d ",ans[i]);
	printf("\n");
}
posted @ 2021-08-12 21:14  fengwu2005  阅读(30)  评论(0编辑  收藏  举报