8.11考试总结(NOIP模拟36)[Dove 打扑克·Cicada 与排序·Cicada 拿衣服]

时间:2021-08-12
本文章向大家介绍8.11考试总结(NOIP模拟36)[Dove 打扑克·Cicada 与排序·Cicada 拿衣服],主要包括8.11考试总结(NOIP模拟36)[Dove 打扑克·Cicada 与排序·Cicada 拿衣服]使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Dove 打扑克

解题思路

考场上是想了一个树状数组维护的打法,但是竟然和 \(qn^2\) 的算法一样是 65pts

暴力就是对于每一次 2 询问重新建一下树状数组,进行计算。。

正解与暴力最大的区别就在于改变了枚举的东西

由枚举每一个堆的大小变为枚举牌堆大小,从同一大小或者不同大小堆的组合中计算答案。

大体思路就是维护一个 set 来保证堆的大小的有序。

同时开一个桶,维护不同大小的堆的个数,进行计算就好了。

在处理询问的时候直接双指针扫就好了。

分别记录当前扫到的点以及符合条件的边界。

code

65pts 树状数组

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,M=3e5+10;
int n,m,fa[N],siz[N],top,sta[N];
bool vis[N];
int find(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=find(fa[x]);
}
struct BIT
{
	int num[N];
	void clear(){memset(num,0,sizeof(num));}
	int lowbit(int x){return x&(-x);}
	void insert(int x,int tmp)
	{
		for(int i=x;i<=n;i+=lowbit(i))
			num[i]+=tmp;
	}
	int query(int x)
	{
		int sum=0;
		for(int i=x;i;i-=lowbit(i))
			sum+=num[i];
		return sum;
	}
}tre;
signed main()
{
	n=read();	m=read();
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;siz[i]=1;
		tre.insert(1,1);
	}
	while(m--)
	{
		int opt,x,y,sum=0;
		opt=read();
		if(opt==1)
		{
			x=read();	y=read();
			int fx=find(x),fy=find(y);
			if(fx==fy)	continue;
			siz[fx]+=siz[fy];
			fa[fy]=fx;
			continue;
		}
		x=read();
		tre.clear();
		top=0;
		for(int i=1;i<=n;i++)
		{
			int tmp=find(i);
			if(vis[tmp])	continue;
			vis[tmp]=true;
			sta[++top]=siz[tmp];
		}
		sort(sta+1,sta+top+1);
		for(int i=1;i<=top;i++)
		{
			if(sta[i]-x>0)
				sum+=tre.query(sta[i]-x);
			tre.insert(sta[i],1);
		}
		for(int i=1;i<=n;i++)
			vis[fa[find(i)]]=false;
		printf("%lld\n",sum);
	}
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,m,all,fa[N],siz[N],sum[N];
multiset<int> s;
int find(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=find(fa[x]);
}
signed main()
{
	n=read();	m=read();
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
		siz[i]=1;
	}
	sum[1]=n;	all=n;
	s.insert(1);
	while(m--)
	{
		int opt,x,y,tot,ans;
		opt=read();
		if(opt==1)
		{
			x=read();	y=read();
			int fx=find(x),fy=find(y);
			if(fx==fy)	continue;
			all--;
			auto it1=s.find(siz[fx]);
			auto it2=s.find(siz[fy]);
			s.erase(it1);
			if(siz[fx]!=siz[fy])	s.erase(it2);
			sum[siz[fx]]--;
			sum[siz[fy]]--;
			if(sum[siz[fx]])	s.insert(siz[fx]);
			if(siz[fx]!=siz[fy]&&sum[siz[fy]])	s.insert(siz[fy]);
			fa[fy]=fx;
			siz[fx]+=siz[fy];
			if(!sum[siz[fx]])	s.insert(siz[fx]);
			sum[siz[fx]]++;
			continue;
		}
		x=read();	tot=all;	ans=0;
		auto l=s.begin(),r=s.begin();
		for(l=s.begin();l!=s.end();l++)
		{
			while((*r)-(*l)<x&&r!=s.end())	tot-=sum[(*r)],r++;
			if(r==s.end())	break;
			if((*l)==(*r))
			{
				ans+=(sum[(*l)]-1)*sum[(*l)]/2;
				tot-=sum[(*r)];	r++;
				if(r==s.end())	continue;
			}
			ans+=sum[(*l)]*tot;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2 Cicada 与排序

解题思路

学习了 cty 的打法后我直接拍手称赞。。

模拟归并排序的过程,并且枚举同一数值的数来自左边区间的个数。

计算这个是总共的相同数值的第几个的概率。

最后根据概率算出期望就好了。

代码轻度压行,自我感觉良好。。。(逃

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=510,mod=998244353;int n,jc[N],vjc[N],inv[N],ans[N],cnt,lsh[N];
struct Node{
	int id,dat,p[N];
	bool friend operator < (Node x,Node y){return x.dat<y.dat;}
}s[N],fb[N];
int ksm(int x,int y){
	int temp=1;
	while(y){
		if(y&1)	temp=temp*x%mod;
		x=x*x%mod;y>>=1;
	}return temp;
}
void init(){
	jc[0]=inv[0]=vjc[0]=1;
	for(int i=1;i<=n;i++)	jc[i]=jc[i-1]*i%mod,inv[i]=inv[i-1]*2%mod;
	vjc[n]=ksm(jc[n],mod-2);	inv[n]=ksm(inv[n],mod-2);
	for(int i=n-1;i>=1;i--)	vjc[i]=vjc[i+1]*(i+1)%mod,inv[i]=inv[i+1]*2%mod;
	sort(lsh+1,lsh+cnt+1);	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++)	s[i].dat=lower_bound(lsh+1,lsh+cnt+1,s[i].dat)-lsh;
}
int C(int x,int y){if(y>x)	return 0;return jc[x]*vjc[y]%mod*vjc[x-y]%mod;}
void merge(int l,int r){
	if(l==r)	return ;
	int mid=(l+r)>>1,len=r-l+1,q[N][2];
	merge(l,mid);	merge(mid+1,r);
	memcpy(fb+l,s+l,sizeof(Node)*len);	memset(q,0,sizeof(q));
	for(int i=l;i<=mid;i++)q[s[i].dat][0]++;
	for(int i=mid+1;i<=r;i++)q[s[i].dat][1]++;
	for(int i=l;i<=r;i++)	for(int j=1;j<=n;j++)s[i].p[j]=0;
	for(int i=l;i<=mid;i++){
		if(!q[s[i].dat][0]||!q[s[i].dat][1])	continue;
		for(int j=1;j<=q[s[i].dat][0];j++){
			for(int k=1;k<=q[s[i].dat][1]+j-1;k++)s[i].p[k]=(s[i].p[k]+C(k-1,j-1)*fb[i].p[j]%mod*inv[k]%mod)%mod;
			for(int k=q[s[i].dat][1];k<=q[s[i].dat][1]+j-1;k++)s[i].p[q[s[i].dat][1]+j]=(s[i].p[q[s[i].dat][1]+j]+C(k-1,q[s[i].dat][1]-1)*fb[i].p[j]%mod*inv[k]%mod)%mod;
		}
	}
	for(int i=mid+1;i<=r;i++){
		if(!q[s[i].dat][0]||!q[s[i].dat][1])	continue;
		for(int j=1;j<=q[s[i].dat][1];j++){
			for(int k=1;k<=q[s[i].dat][0]+j-1;k++)s[i].p[k]=(s[i].p[k]+C(k-1,j-1)*fb[i].p[j]%mod*inv[k]%mod)%mod;
			for(int k=q[s[i].dat][0];k<=q[s[i].dat][0]+j-1;k++)s[i].p[q[s[i].dat][0]+j]=(s[i].p[q[s[i].dat][0]+j]+C(k-1,q[s[i].dat][0]-1)*fb[i].p[j]%mod*inv[k]%mod)%mod;
		}
	}
	for(int i=l;i<=r;i++)for(int j=1;j<=n;j++)if(!s[i].p[j])	s[i].p[j]=fb[i].p[j];
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		lsh[++cnt]=s[i].dat=read();s[i].id=i;
		for(int j=1;j<=n;j++)s[i].p[j]=1;
	}
	init();merge(1,n);sort(s+1,s+n+1);
	for(int i=1;i<=n;i++){
		int j=i;	while(s[j+1].dat==s[i].dat)	j++;
		for(int k=i;k<=j;k++)for(int l=i;l<=j;l++)ans[s[k].id]=(ans[s[k].id]+l*s[k].p[l-i+1]%mod)%mod;i=j;
	}
	for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
	return 0;
}

T3 Cicada 拿衣服

题解

\(n^2\) 可以过 30000 的数据。。

我又用线段树手动套了一个 log 卡一卡范围就好了

(数据点分治) (雾

code

28pts 线段树

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=5e5+10,INF=1e18,base=2147483647;
int n,m,ans[N],s[N];
struct Segment_Tree
{
	int mx,mn,ad,o;
}tre[N<<2];
void push_up(int x)
{
	tre[x].ad=tre[ls].ad&tre[rs].ad;
	tre[x].o=tre[ls].o|tre[rs].o;
	tre[x].mn=min(tre[ls].mn,tre[rs].mn);
	tre[x].mx=max(tre[ls].mx,tre[rs].mx);
}
void build(int x,int l,int r)
{
	if(l==r)
	{
		tre[x].o=tre[x].mx=tre[x].mn=tre[x].ad=s[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	push_up(x);
}
int query_min(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].mn;
	int mid=(l+r)>>1,ans1=INF,ans2=INF;
	if(L<=mid)	ans1=query_min(ls,l,mid,L,R);
	if(R>mid)	ans2=query_min(rs,mid+1,r,L,R);
	return min(ans1,ans2);
}
int query_max(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].mx;
	int mid=(l+r)>>1,ans1=0,ans2=0;
	if(L<=mid)	ans1=query_max(ls,l,mid,L,R);
	if(R>mid)	ans2=query_max(rs,mid+1,r,L,R);
	return max(ans1,ans2);
}
int query_or(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].o;
	int mid=(l+r)>>1,ans1=0,ans2=0;
	if(L<=mid)	ans1=query_or(ls,l,mid,L,R);
	if(R>mid)	ans2=query_or(rs,mid+1,r,L,R);
	return ans1|ans2;
}
int query_and(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].ad;
	int mid=(l+r)>>1,ans1=base,ans2=base;
	if(L<=mid)	ans1=query_and(ls,l,mid,L,R);
	if(R>mid)	ans2=query_and(rs,mid+1,r,L,R);
	return ans1&ans2;
}
signed main()
{
	int sum=0;
	n=read();	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	build(1,1,n);
	for(int len=n;len>=1;len--)
	{
		if(sum==n)	break;
		for(int l=1;l+len-1<=n;l++)
		{
			if(sum==n)	break;
			int r=l+len-1;
			int mn=query_min(1,1,n,l,r);
			int mx=query_max(1,1,n,l,r);
			int o=query_or(1,1,n,l,r);
			int ad=query_and(1,1,n,l,r);
			int temp=mn+o-mx-ad;
			if(temp>=m)
				for(int i=l;i<=r;i++)
				{
					if(!ans[i])	sum++;
					ans[i]=max(ans[i],r-l+1);
				}
		}
	}
	for(int i=1;i<=n;i++)
		if(ans[i])	printf("%lld ",ans[i]);
		else	printf("%lld ",-1ll);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e6+10,INF=1e18,base=2147483647;
int n,m,ans[N],s[N];
signed main()
{
	int sum=0;
	n=read();	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int l=1;l<=n;l++)
	{
		int pos=-1,maxn;
		int mn=s[l],mx=s[l],o=s[l],ad=s[l];
		if(mn+o-mx-ad>=m)	pos=l;
		maxn=mn+o-mx-ad;
		for(int r=l+1;r<=n;r++)
		{
			mn=min(mn,s[r]);
			mx=max(mx,s[r]);
			o|=s[r];
			ad&=s[r];
			int temp=mn+o-mx-ad;
			maxn=max(maxn,temp);
			if(temp>=m)	pos=r;
			if(n<=30000)	continue;
			if(r-l+1>700)	break;
		}
		if(pos==-1)	continue;
		for(int i=l;i<=pos;i++)
			ans[i]=max(ans[i],pos-l+1);
	}
	for(int i=1;i<=n;i++)
		if(ans[i])	printf("%lld ",ans[i]);
		else	printf("%lld ",-1ll);
	return 0;
}

原文地址:https://www.cnblogs.com/Varuxn/p/15134446.html