1. 程式人生 > 其它 >noip模擬測試36

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;
}