noip模拟测试36

这次考试,题目比较有难度,考场上我只有T1有65分的思路,T3有一个暴力的思路,T2完全不会打,但是这次我把自己应该得到的分数都拿到了,也算是一个进步吧。

T1 Dove 打扑克

这道题,考试时我想到可以用一个权值线段树的思想,查询当前位置之前的与当前数值差值为 x的数的个数,每次重构一颗线段树 nlogn 进行计算,但是这只能拿到65分,算法的瓶颈在于我每次都重构了一颗线段树,我当时想半天如何才能不重构树,但没什么思路,那么正解就和我的思路不太一样,正解是维护了两个东西,size[i],表示 i 位置的大小,cnt[i] 表示这个大小的数量,那么我们就可以通过这两个数组,再利用一个前缀和就可以 o(qn) 得到结果,但是这样只能得到80分,因为我们还可以再优化,介绍一个引理:
给定n个非负整数组成的可重集合,{x1,...,xn},满足\(\sum\limits_{i=1}^{n}x_i=m\),那么一定有
\(diff_x\) <=\(sqrt(m)\), \(diff_x\)表示集合中本质不同的数字的数量。
知道了这个结论,那么这道题就可以进行优化了,因为当前人数不同的组的数量不超过\(sqrt(n)\),所以我们可以使用一个set单点插入,删除,并在查询过程中使用一个vector,记录前缀和,这样我们就可以AC了,总复杂度 \(o\times sqrt(n) \times log(sqrt(n))\)
代码如下:

65pts_code


#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=201000;
int n,m,ans;
int size[N],be[N],vis[N];
bool no[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
ii gett(int x)
{
	if(x==be[x])
		return x;
	return be[x]=gett(be[x]);
}
struct Segment_Tree
{
	int sum[360010];
	iv pp(int rt)
	{
		sum[rt]=sum[lc]+sum[rc];
	}
	iv insert(int rt,int l,int r,int p)
	{
		if(l==r)
		{
			sum[rt]++;
			return;
		}
		if(mid>=p)
			insert(lc,l,mid,p);
		else
			insert(rc,mid+1,r,p);
		pp(rt);
	}
	ii query(int rt,int l,int r,int L,int R)
	{
		if(L>R)
			return 0;
		if(L<=l&&r<=R)
			return sum[rt];
		if(mid>=R)
			return query(lc,l,mid,L,R);
		if(mid<L)
			return query(rc,mid+1,r,L,R);
		return query(lc,l,mid,L,R)+query(rc,mid+1,r,L,R);
	}
	iv ql(int rt,int l,int r)
	{
		sum[rt]=0;
		if(l==r)
			return;
		ql(lc,l,mid);
		ql(rc,mid+1,r);
	}
}T;
signed main()
{
	int opt,x,y,fx,fy,fi;
	n=read();
	m=read();
	for(re i=1;i<=n;i++)
		size[i]=1;
	for(re i=1;i<=n;i++)
		be[i]=i;
	while(m--)
	{
		opt=read();
		if(opt==1)
		{
			x=read();
			y=read();
			fx=gett(x);
			fy=gett(y);
			if(fx==fy)
				continue;
			be[fy]=fx;
			size[fx]+=size[fy];
			size[fy]=0;
		}
		else
		{
			T.ql(1,1,n);
			x=read();
			ans=0;
			if(x!=0)
			{
				for(re i=1;i<=n;i++)
				{
					
					fi=gett(i);
					if(vis[fi]==m)
						continue;
					ans+=T.query(1,1,n,size[fi]+x,n);
					if(size[fi]-x>0)
						ans+=T.query(1,1,n,1,size[fi]-x);
					T.insert(1,1,n,size[fi]);
					vis[fi]=m;
				}
			}
			else
			{
				for(re i=1;i<=n;i++)
				{
					
					fi=gett(i);
					if(vis[fi]==m)
						continue;
					ans+=T.query(1,1,n,size[fi],n);
					ans+=T.query(1,1,n,1,size[fi]-1);
					T.insert(1,1,n,size[fi]);
					vis[fi]=m;
				}
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

AC_code


#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=201000;
struct node
{
	int posi,sum;
	friend bool operator < (node a,node b)
	{
		return a.posi < b.posi;
	}
};
set<int> T;
int n,m,ans;
int size[N],be[N],vis[N],cnt[N],p[N];
bool no[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
ii gett(int x)
{
	if(x==be[x])
		return x;
	return be[x]=gett(be[x]);
}
signed main()
{
	int opt,x,y,z,fx,fy,fi,zero;
	n=read();
	m=read();
	T.insert(1);
	cnt[1]=n;
	for(re i=1;i<=n;i++)
	{
		be[i]=i;
		size[i]=1;	
	}
	while(m--)
	{
		ans=0;
		opt=read();
		if(opt==1)
		{
			x=read();
			y=read();
			fx=gett(x);
			fy=gett(y);
			if(fx==fy)
				continue;		
			cnt[size[fx]]--;
			cnt[size[fy]]--;
			if(!cnt[size[fx]])
				T.erase(size[fx]);
			if(!cnt[size[fy]])
				T.erase(size[fy]);
			size[fx]+=size[fy];
			if(T.find(size[fx])==T.end())
				T.insert(size[fx]);
			cnt[size[fx]]++;
			be[fy]=fx;
			size[fy]=0;	
		}
		else
		{
			x=read();
			zero=0;
			if(x==0)
			{
				int col=0;
				for(auto pos:T)
				{
					ans+=zero*cnt[pos];
					ans+=(cnt[pos])*(cnt[pos]-1)/2;
					zero+=cnt[pos];
				}
			}
			else
			{
				vector<node>v;
				for(auto pos:T)
				{
					zero+=cnt[pos];
					v.push_back((node){pos,zero});
					if(pos>x)
					{
						int o=upper_bound(v.begin(),v.end(),(node){pos-x,0})-v.begin();
						if(o-1>=0)
							ans+=v[o-1].sum*cnt[pos];
					}
				}
			}	
			printf("%lld\n",ans);		
		}
	}
	return 0;
}

T2 Cicada 与排序

思路:我们设\(f_{i,j,k}\),表示第 i 层,原来的位置为 j ,排序后的位置为 k 的概率,那么答案很好求。现在的问题是如何进行状态转移,我们引入一个辅助数组\(g_{i,j}\)表示归并排序过程中两个指针分别指向i,j的概率,那么我们根据归并排序的过程进行转移即可。具体实现见代码:

AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int mo=998244353;
const int inv=499122177;
const int N=510;
int n;
int a[N];
int f[N][N][N],g[N][N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
iv merge(int rt,int l,int r)
{
	if(l==r)
	{
		f[rt][l][r]=1;
		return;
	}
	merge(rt+1,l,mid);
	merge(rt+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)
				g[i][j+1]=(g[i][j+1]%mo+g[i][j]%mo)%mo;
			else if(j==r-mid)
				g[i+1][j]=(g[i+1][j]%mo+g[i][j]%mo)%mo;
			else if(a[l+i]>a[mid+j+1])
				g[i][j+1]=(g[i][j+1]%mo+g[i][j]%mo)%mo;
			else if(a[l+i]<a[mid+j+1])
				g[i+1][j]=(g[i+1][j]%mo+g[i][j]%mo)%mo;
			else
			{
				g[i][j+1]=(g[i][j+1]%mo+g[i][j]%mo*inv%mo)%mo;
				g[i+1][j]=(g[i+1][j]%mo+g[i][j]%mo*inv%mo)%mo;
			}	
		}
	}
	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 and k==r-mid)
					continue;
				if(j==mid-l+1)
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][mid+k+1]%mo*g[j][k]%mo)%mo;
				else if(k==r-mid)
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][j+l]%mo*g[j][k]%mo)%mo;
				else if(a[j+l]>a[mid+k+1])
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][mid+k+1]%mo*g[j][k]%mo)%mo;
				else if(a[j+l]<a[mid+k+1])
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][j+l]%mo*g[j][k]%mo)%mo;
				else
				{
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][j+l]%mo*g[j][k]%mo*inv%mo)%mo;
					f[rt][i][j+l+k]=(f[rt][i][j+l+k]+f[rt+1][i][mid+k+1]%mo*g[j][k]%mo*inv%mo)%mo;
				}
			}
		}
	}
	sort(a+l,a+r+1);
}
signed main()
{
	n=read();
	for(re i=1;i<=n;i++)
		a[i]=read();
	merge(1,1,n);
	for(re i=1;i<=n;i++)
	{
		int ans=0;
		for(re j=1;j<=n;j++)
			ans=(ans%mo+f[1][i][j]%mo*j%mo)%mo;
		printf("%lld ",ans);
	}
	return 0;
}

T3 Cicada 拿衣服

思路:\(n^2\)暴力+乱搞,首先说一下暴力,我们以每个点为左端点向右查询满足条件的右端点,然后每次都进行一次更新答案的过程,这样就可以拿到64分,然后进行乱搞,猜测答案不会很大,于是我们枚举的右端点就到 i+700 ,即可通过此题......

AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1010000;
const int INF=1e9;
int n,k;
int ans[N],a[N];
ii read()
{
	int x=0;
	bool f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')
			f=0;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return f?x:(-x);
}
iv spe_1()
{
	for(re i=1;i<=n;i++)
	{
		int minn=INF,maxx=0,Or=0,And=(1<<30)-1,r=-INF;
		for(re j=i;j<=n;j++)
		{
			Or|=a[j];
			And&=a[j];
			if(a[j]>maxx)
				maxx=a[j];
			if(a[j]<minn)
				minn=a[j];
			if(minn-maxx+Or-And>=k)
				r=j;
		}
		int len=r-i+1;
		for(re j=i;j<=r;j++)
			if(ans[j]<len)
				ans[j]=len;
	}
}
iv spe_2()
{
	for(re i=1;i<=n;i++)
	{
		int minn=INF,maxx=0,Or=0,And=(1<<30)-1,r=-INF;
		for(re j=i;j<=min(n,700+i);j++)
		{
			Or|=a[j];
			And&=a[j];
			if(a[j]>maxx)
				maxx=a[j];
			if(a[j]<minn)
				minn=a[j];
			if(minn-maxx+Or-And>=k)
				r=j;
		}
		int len=r-i+1;
		for(re j=i;j<=r;j++)
			if(ans[j]<len)
				ans[j]=len;
	}
}
signed main()
{
	n=read();
	k=read();
	for(re i=1;i<=n;i++)
	{
		a[i]=read();
		ans[i]=-1;
	}
	if(n<=30000)
		spe_1();
	else
		spe_2();
	for(re i=1;i<=n;i++)
		printf("%lld ",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/WindZR/p/15130408.html