1. 程式人生 > 其它 >NOIP模擬21:「Median·Game·Park」

NOIP模擬21:「Median·Game·Park」

T1:Median

  線性篩+桶+隨機化(??什麼鬼?)。
  首先,題解一句話秀到了我:

考慮輸入如此詭異,其實可以看作隨機資料

  隨機資料??
  這就意味著分佈均勻。。
  又考慮到w<=k<=n
  可以用桶了。
  中位數暴力算的話是排序後取中間。
  但是時間明顯不允許。只能\(O(n)\)過掉。所以要維護兩個中位數指標(k%2==1當然就是一個了)。
  由於資料隨機,分佈均勻,所以可以直接跳桶。
  笑死,我當時不信還把資料輸了出去,發現有的相鄰資料差了幾百,就這還能直接跳。。。。。好吧,我膚淺了。。。。
  差點沒調出來的程式碼:

200行的煌煌大作QWQ
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define ll long long
	#define rr register
	const int N=1.1e7;
	const int MAXN=1.8e8+3;
	int n,k;
	int w;
	ll cnt,prime[N];
	bool notprime[MAXN];
	double ans;
	int s2[N];
	int ton[N<<1];
	inline void Prime()
	{
		for(rr int i=2;i<MAXN;i++)
		{
			if(!notprime[i]) prime[++cnt]=i;
			for(rr int j=1;j<=cnt&&i*prime[j]<MAXN;j++)
			{
				notprime[i*prime[j]]=1;
				if(!(i%prime[j]))
					break;
			}
		}
	}
	inline int read()
	{
		rr int x_read=0,y_read=1;	
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}	
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read*10)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
};
using namespace STD;
int main()
{
	Prime();
	n=read(),k=read(),w=read();
	for(rr int i=1;i<=n;i++)
	{
		prime[i]=prime[i]*i%w;
		s2[i]=prime[i]+prime[i/10+1];
	}
	if(k&1)
	{
		int *p=ton+s2[1],l=1,sum=1;
		ton[s2[1]]++;
		for(rr int i=2;i<=k;i++)
		{
			sum++;
			ton[s2[i]]++;
			if(s2[i]<=(p-ton)) l++;
			if(l<((sum>>1)+1))
				while(l<((sum>>1)+1))
				{
					p++;
					while(!(*p)) p++;
					l+=(*p);
				}
			else
				while(l-(*p)>=((sum>>1)+1))
				{
					l-=(*p);
					p--;
					while(!(*p)) p--;
				}	
		}
		ans+=(p-ton);
		for(rr int i=k+1;i<=n;i++)
		{
			ton[s2[i]]++;
			if(s2[i]<=p-ton) l++;
			ton[s2[i-k]]--;
			if(s2[i-k]<=p-ton) l--;
			if(l<((k>>1)+1))
				while(l<((k>>1)+1))
				{
					p++;
					while(!(*p)) p++;
					l+=(*p);
				}
			else
				while(l-(*p)>=((k>>1)+1))
				{
					l-=(*p);
					p--;
					while(!(*p)) p--;
				}
			ans+=(p-ton);
		}
		printf("%.1lf\n",ans);
	}
	else
	{
		int *p1=ton+min(s2[1],s2[2]),*p2=ton+max(s2[1],s2[2]),l2=2,l1=1,sum=2;
		ton[s2[1]]++;
		ton[s2[2]]++;
		for(rr int i=3;i<=k;i++)
		{
			sum++;
			ton[s2[i]]++;
			if(s2[i]<=(p1-ton)) l1++;
			if(s2[i]<=(p2-ton)) l2++;
			if(l2<((sum>>1)+1))
				while(l2<((sum>>1)+1))
				{
					p2++;
					while(!(*p2)) p2++;
					l2+=(*p2);
				}
			else
				while(l2-(*p2)>=((sum>>1)+1))
				{
					l2-=(*p2);
					p2--;
					while(!(*p2)) p2--;
				}
			if(l1<(sum>>1))
				while(l1<(sum>>1))
				{
					p1++;
					while(!(*p1)) p1++;
					l1+=(*p1);
				}
			else
				while(l1-(*p1)>=(sum>>1))
				{
					l1-=(*p1);
					p1--;
					while(!(*p1)) p1--;
				}
		}
		double temp=((p1-ton)+(p2-ton));
		ans+=(temp/2.00);
		for(rr int i=k+1;i<=n;i++)
		{
			ton[s2[i]]++;
			if(s2[i]<=(p1-ton)) l1++;
			if(s2[i]<=(p2-ton)) l2++;
			ton[s2[i-k]]--;
			if(s2[i-k]<=p1-ton) l1--;
			if(s2[i-k]<=p2-ton) l2--;
			if(l2<((k>>1)+1))
				while(l2<((k>>1)+1))
				{
					p2++;
					while(!(*p2)) p2++;
					l2+=(*p2);
				}
			else
				while(l2-(*p2)>=((k>>1)+1))
				{
					l2-=(*p2);
					p2--;
					while(!(*p2)) p2--;
				}
			if(l1<(k>>1))
				while(l1<(k>>1))
				{
					p1++;
					while(!(*p1)) p1++;
					l1+=(*p1);
				}
			else
				while(l1-(*p1)>=(k>>1))
				{
					l1-=(*p1);
					p1--;
					while(!(*p1)) p1--;
				}
			temp=(p1-ton)+(p2-ton);
			ans+=temp/2.00;
		}
		printf("%.1lf\n",ans);
	}
}

  其他的事情
  在討論時土哥提到了一個叫”對頂堆”的東西來維護中位數,當然,\(O(nlogn)\)會TLE。
  這個東西其實就是維護兩個堆,一個大根堆,一個小根堆。
  小根堆裡的數全部大於大根堆裡的數,這樣就有單調性了,相當於排了個序。
  但比直接用排序演算法在N上少了個指數。
  當有數進來時,先與兩個堆頂比較如果大於大根堆頂就進小根堆,否則進大根堆。
  然後比較兩個堆的大小,然後將多的數放進另一個堆即可。
  中位數就是堆頂之和除以2
  至於說k%2==1的情況,你就不要把中位數往堆裡放即可。

T2:Game

  考場上一眼看出來就是貪心,直接放了個堆上去,還納悶為啥這麼簡單呢。。
  然後T了。
  正解是\(O(nk)\)


  還是桶。一場考試三道題,兩道考桶。。。。
  這題有個看起來很明顯但是你往往會忽視的性質:

如果你拿進序列的數比當前序列裡的最大值還要大,那麼它下一輪一會被拿走

  簡單到無需證明。。
  但是他會決定你是\(A\)還是\(T\)
  記得開longlong。

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define ll long long
	#define rr register 	
	#define inf INT_MAX
	const int N=100004;
	const int K=2004;
	int n,k,p;
	int *po;
	ll score[2];
	ll a[N];
	int b[N];
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read*10)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
};
using namespace STD;
int main()
{
	n=read(),k=read();
	for(rr int i=1;i<=n;i++)
		a[i]=read();
	while(k--)
	{
		int p=read();
		ll temp=-inf;
		int roun=1;
		int now=p;
		for(rr int i=1;i<=p;i++)
		{
			temp=max(temp,a[i]);
			b[a[i]]++;
		}
		po=b+temp;	
		temp=-inf;
		while(roun<=n)
		{
			if(temp>(po-b))
			{
				score[roun&1]+=temp;
				temp=-inf;
			}
			else
			{
				score[roun&1]+=(po-b);
				int x=*po;
				x--;
				*po=x;
			}
			now++;
			if(now<=n)
			{
				if(a[now]>(po-b))
					temp=a[now];
				else
					b[a[now]]++;
			}
			while(((*po)==0)&&(po>b))
				po--;
			roun++;	
		}
		printf("%lld\n",score[1]-score[0]);
		score[0]=score[1]=0;
	}
}

T3:Park

  還在推方程,先鴿掉好了。