noip模拟12

编号有些错乱aaa

早上打着哈欠走进机房,想着今天第一次和衡中联考,可不能挂
于是经过三个半小时的浴血奋战,终于爆成了暴力分:

pic.PNG

考出了有史以来最差的成绩(虽然以前机房没有11个人


考场上先乖乖地看完了所有题,曾经有那么一瞬间觉得 \(t3\) 好像比较可做,但是按照常理还是先搞 \(t1\) 吧……
\(t1\) 弄了一个小时觉得单调栈+主席树特别靠谱,但是显然并不会算复杂度
上厕所的时候曾经想到可以判断左右区间大小来节省时间,但是觉得这好像用处不大于是弃掉了……(事实证明rk1巨佬就是这样A掉的)
然后 \(t2\) 写了个暴搜开始迷信打表找规律
然后还有40分钟开始看 \(t3\),突然发现异常的可做,然后开始狂写,写啊写啊眼看着时间不够了,走后只得了个 \(puts\)("\(-1\)") 的暴力分……

A. 简单的区间

题目的柿子转化成:\(sum[i]-sum[j-1]=a[k] (\mod k)\)
可以用单调栈处理出每个数作为最大值的区间,枚举这个数k,枚举 \(i\)\(j\) 中的一个,这个根据哪边的点少枚举哪边,然后需要 \(log\) 找出另一半满足条件的 \(sum\) 的个数,这个相当于静态区间查找问题,可以用主席树解决
由于主席树常数巨大,chy想出了另一个处理办法,可以将每次查询都记录下来,即左右端点和值,然后利用容斥思想,再将区间转化成两部分,这样只需要查询1到x的信息即可
可以按位置排序,然后开桶不断加入新数,到达位置时更新答案
由于区间有 \(nlogn\) 个,相当于与主席树同阶,如果愿意可以桶排做到总复杂度 \(nlogn\)

这道题的正解给的是分治,每次将区间在区间最大值处分开,统计跨过最大值的点对数量,统计方法和前面说的一样

代码实现
#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();	
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
const int maxn=2e6+5;
int n,k,a[maxn],sum[maxn],l[maxn],r[maxn],root[maxn],sta[maxn],tp,tot,vis[maxn];
long long ans;
struct Ask{
	int pos,val,op;	
	Ask(){}
	Ask(int x,int y,int m):pos(x),val(y),op(m){}
}ask[maxn*20];
bool cmp(Ask a,Ask b){
	return a.pos<b.pos;	
}
int main(){
	n=read();
	k=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		sum[i]=(sum[i-1]+(a[i]%k))%k;
	}
	for(int i=1;i<=n;i++){
		while(tp&&a[i]>a[sta[tp]])r[sta[tp]]=i-1,tp--;
		l[i]=sta[tp]+1;
		sta[++tp]=i;	
	}
	while(sta[tp])r[sta[tp]]=n,tp--;
	for(int i=1;i<=n;i++){
		int x=a[i]%k;
		if(i-l[i]>r[i]-i)
			for(int j=i;j<=r[i];j++){
				int y=(sum[j]-x+k)%k;
				ask[++tot]=Ask(l[i]-2,y,-1),ask[++tot]=Ask(i-1,y,1);
			}
		else
			for(int j=i-1;j>=l[i]-1;j--){
				int y=(sum[j]+x)%k;
				ask[++tot]=Ask(i-1,y,-1),ask[++tot]=Ask(r[i],y,1);
			}		
	}
//	for(int i=1;i<=tot;i++){
//		cout<<ask[i].pos<<" "<<ask[i].val<<" "<<ask[i].op<<endl;	
//	}
	sort(ask+1,ask+tot+1,cmp);
	int tp=1;
	while(ask[tp].pos<0&&tp<=tot)ans+=ask[tp].op*vis[ask[tp].val],tp++;
	vis[0]++;
	while(ask[tp].pos<=0&&tp<=tot)ans+=ask[tp].op*vis[ask[tp].val],tp++;
	for(int i=1;i<=n;i++){
		vis[sum[i]]++;
		while(ask[tp].pos<=i&&tp<=tot)ans+=ask[tp].op*vis[ask[tp].val],tp++;
	}
	cout<<ans-n;
	return 0;
}

B. 简单的玄学

确实是玄学……

这道题我连容斥都没想到
题目问出现相同数的个数,可以统计都不同的个数
相当于 \(n\) 个数的 \(m\) 排列,即 \(A_{2^n}^m\)
总方案数显然为 \({2^{n}}^m\)
然后考虑怎么计算:题目要求先约分再取模,这显然与先取模再约分不是一回事
如果要约分只能越好多好多个2
于是问题在于怎样统计出分子有多少个因子2
把分子拆开:\(2^n*(2^n-1)*(2^n-2)*(2^n-3)……(2^n-m+1)\)
上面每个括号里的2的次方数取决于后面减数里能提出多少的公因数,于是进一步问题转化为 \((m-1)!\) 里2的次数是多少
据说这是个经典的问题?
是这样计算的:首先将数除以2,累加到答案中,这一步相当于把奇数剔除,统计偶数个数,继续除以2,累计答案,这一步相当于把是2的倍数不是4的倍数的剔除,统计4的倍数的个数,以此类推
统计完这个就可以愉快的约分了
对于分子提出公因数后还剩下多少——这道题的模数看着是不是特别的神奇?这是精心设计过的,因为分子是连续的m个数,当m>mod时,那么当中必有一个是 mod 的倍数,分子就是0了,m较小的时候直接暴力计算即可

代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,cnt,x,y,tp,mul;
const int mod=1e6+3;
int po(int a,int b=mod-2){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;	
	}
	return ans;
}
signed main(){
	cin>>n>>m;
	int sum=m-1;
	while(sum){
		sum>>=1ll;
		cnt+=sum;	
	}
	cnt+=n;
	x=y=po(po(2,n),m)%mod;
	if(m<mod){
		tp=po(2,n);
		mul=1;
		for(int i=1;i<=m;i++){
			mul=mul*tp%mod;
			tp--;
			if(tp<0)tp+=mod;
		}
		x=(x-mul+mod)%mod;
	}
	x=x*po(po(2ll,cnt))%mod;
	y=y*po(po(2,cnt))%mod;
	cout<<x<<" "<<y<<endl;
	return 0;
}

C. 简单的填数

这道题算是本场考试思路最正确的一道题了,只不过这种方法代码写起来比较恶心~
\(f[i][j]\) 表示第 i 个已知数是第 j 个连续数是否可行,这是一个布尔变量,第二维开 [1,5]
\(f[i][j]\) 要从 \(f[i-1][k]\) 转移,转移条件是这样的:首先统计出两点之间没出现的颜色数,并处理出安排好这些颜色的最小区间长度和最大区间长度,如果它们之间的距离在这个范围内则可以转移。最小值为颜色数乘2,如果 \(k==1\) 加1,最大值为颜色数乘5加 \(5-k\),并且都要加上 \(j-1\)
这样可以统计出最后一个出现的位置是第几个可行,分两种情况讨论,如果大于等于2,则从下一个开始颜色不同,并每个颜色都有两个(当然要特判一下最后一种颜色也要有匹配);只能等于1的话则下一个必须染一样的颜色,之后再按上面的做
至于构造方案,转移是记录当前状态是由哪个状态转移而来的,现将两端点的要求满足,中间每种颜色都安排两个位置,如果位置还有剩余,不超过5个随便安排即可。

代码实现
#include<bits/stdc++.h>
using namespace std;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();	
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
const int maxn=2e5+5;
int n,a[maxn],tot,b[maxn],ans[maxn],sta[maxn],tp;
bool f[maxn][10];
pair<int,int>from[maxn][10];
int main(){
	n=read();
	a[++tot]=1;
	for(int i=1;i<=n;i++){
		b[i]=read();
		if(i!=1){
			if(b[i])a[++tot]=i;
		}
	}
	b[1]=1;
	f[1][1]=true;
	for(int i=2;i<=tot;i++){
		int j=i-1;
			int num=a[i]-a[j]-1;
			int c=b[a[i]]-b[a[j]]-1;
			for(int k=1;k<=5;k++){
				if(f[j][k]){
					if(c==-1){
						if(k+num+1<=5){
							f[i][k+num+1]=true;
							from[i][k+num+1]=make_pair(j,k);
						}
					}
					for(int p=1;p<=5;p++){
						if((p-1+2*c+((k==1)?1:0))<=num&&p-1+5*c+5-k>=num){
							f[i][p]=true;
							from[i][p]=make_pair(j,k);
						}
					}
				}
			}
	}
	int flag=0;
	for(int k=5;k>=2;k--){
		if(f[tot][k]){
			flag=k;
			break;
		}
	}	
	if(flag){
		cout<<(n-a[tot])/2+b[a[tot]]<<endl;
		ans[n]=(n-a[tot])/2+b[a[tot]];
		int q=b[a[tot]]+1,last=a[tot];
		for(int i=a[tot]+1;i<=n-1;i+=2)ans[i]=ans[i+1]=q,q++,last=i+1;
		for(int i=last+1;i<=n-1;i++)ans[i]=ans[n];
		
		for(int i=tot,j=flag;i>=1;i--){
			ans[a[i]]=b[a[i]];
			if(i==1)break;
			int x=i-1;
			int y=from[i][j].second;
			int num=a[i]-a[x]-1;
			tp=0;
			for(int k=1;k<=j-1;k++)sta[++tp]=b[a[i]],num--;
			for(int k=b[a[x]]+1;k<=b[a[i]]-1;k++){
				sta[++tp]=k;
				sta[++tp]=k;
				num-=2;
			}
			if(num){
				if(y==1)sta[++tp]=b[a[x]],num--;
				if(num)
				for(int k=b[a[x]]+1;k<=b[a[i]]-1;k++){
					sta[++tp]=k;
					num--;
					if(!num)break;
					sta[++tp]=k;
					num--;
					if(!num)break;
					sta[++tp]=k;
					num--;
					if(!num)break;
				}
			}
			if(num){
				for(int k=1;k<=num;k++)sta[++tp]=b[a[x]];
			}
//			cout<<tp<<" "<<a[i]<<" "<<a[x]<<endl;
			sort(sta+1,sta+tp+1);
			for(int k=a[i]-1;k>=a[x]+1;k--)ans[k]=sta[tp--];
			j=y;
		}
	}
	else{
		if(f[tot][1]){
//		cout<<"hhh ";
			cout<<(n-a[tot]-1)/2+b[a[tot]]<<endl;
			ans[n]=(n-a[tot]-1)/2+b[a[tot]];
			if(a[tot]!=n-1){
				int q=b[a[tot]]+1,last=a[tot]+1;
				
				ans[a[tot]]=b[a[tot]];
				for(int i=a[tot]+2;i<=n-1;i+=2)ans[i]=ans[i+1]=q,q++,last=i+1;
				for(int i=last+1;i<=n-1;i++)ans[i]=ans[n];
			}
//			cout<<"hhh "<<tot<<endl;
			for(int i=tot,j=1;i>=1;i--){
				ans[a[i]]=b[a[i]];
				if(i==1)break;
				int x=i-1;
				int y=from[i][j].second;
				int num=a[i]-a[x]-1;
	//			cout<<"ppp "<<i<<" "<<j<<endl;
				tp=0;
				for(int k=1;k<=j-1;k++)sta[++tp]=b[a[i]],num--;
				for(int k=b[a[x]]+1;k<=b[a[i]]-1;k++){
					sta[++tp]=k;
					sta[++tp]=k;
					num-=2;
				}
	// 			cout<<num<<endl;
				if(num){
					if(y==1)sta[++tp]=b[a[x]],num--;
					if(num)
					for(int k=b[a[x]]+1;k<=b[a[i]]-1;k++){
						sta[++tp]=k;
						num--;
						if(!num)break;
						sta[++tp]=k;
						num--;
						if(!num)break;
						sta[++tp]=k;
						num--;
						if(!num)break;
					}
				}
				if(num){
					for(int k=1;k<=num;k++)sta[++tp]=b[a[x]];
				}
				sort(sta+1,sta+tp+1);
				for(int k=a[i]-1;k>=a[x]+1;k--)ans[k]=sta[tp--];
				j=y;
			}
		}
		else {
			cout<<-1<<endl;
			return 0;
		}
	}
	for(int i=1;i<=n;i++)printf("%d ",ans[i]);
	return 0;
}
posted @ 2021-07-12 18:23  y_cx  阅读(60)  评论(1编辑  收藏  举报