noip模擬45[真是啥也不會]

fengwu2005發表於2021-08-22

noip模擬45 solutions

真是一個題都不會了,然而考完試之後我在10min之內切掉了最後一個題

話說這是為什麼呢,

因為最後一個是回滾莫隊的大板子,然而我忘記了,不不不,是沒有記起來過

T1 打表

好像還真的是一個打表題,找找規律就能過,真的

結論就是
\(\huge res=\frac{\sum\limits^{n}_{i=1}abs(x_i-x_{ans})}{2^k}\)

這個是可以證出來的,

首先對於只剩下一位沒有選的時候,肯定是一個CPU選1,另外一個選0

我們考慮剩下更多的情況,可以知道每次兩個CPU都會選擇同一位進行操作,並選擇不同的數

所以期望仍然是一半一半,所以還是剩下的數的差的平均數

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int 
#define ll long long
const int N=1<<18;
const ll mod=1e9+7;
ll k,ans,a[N],res;
ll ksm(ll x,ll y){
	ll ret=1;
	while(y){
		if(y&1)ret=ret*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ret;
}
signed main(){
	scanf("%lld%lld",&k,&ans);
	for(re i=0;i<(1<<k);i++)scanf("%lld",&a[i]);
	ans=a[ans];
	for(re i=0;i<(1<<k);i++)res=(res+abs(a[i]-ans))%mod;
	printf("%lld",res*ksm(ksm(2ll,k),mod-2)%mod);
}

其實考場上沒有看懂題,還找人講了一下

T2 蛇

好象是CF的原題,我也沒做過,暴搜還打錯了,不知道為啥,不打了,直接正解

肯定是扭來扭去的走的,最後接上兩個平著的

noip模擬45[真是啥也不會]

那我們就設dp[i][j][k][0/1]表示在第i行,第j列,匹配到了第k個字元

0表示可以豎著轉移,1表示不可以

我們直接列舉下一個字元,轉移就行了

前面的那個橫著的直接預處理就好了,後面的等到dp完了之後再處理

這個處理就用hash就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ull unsigned long long
#define ll long long
const ull bas=131;
const int N=2005;
const ll mod=1e9+7;
ll n,m,dp[2][N][N][2],ans;
char ch[2][N],a[N];
ull hc[2][2][N],ha[N],ba[N];
ull get_hc0(int x,int l,int r){return hc[x][0][r]-hc[x][0][l-1]*ba[r-l+1];}
ull get_hc1(int x,int l,int r){return hc[x][1][l]-hc[x][1][r+1]*ba[r-l+1];}
ull get_ha(int l,int r){return ha[r]-ha[l-1]*ba[r-l+1];}
void get_ans(){
	memset(dp,0,sizeof(dp));
	for(re i=0;i<=1;i++)for(re j=1;j<=n;j++)hc[i][0][j]=hc[i][0][j-1]*bas+ch[i][j]-'a';
	for(re i=0;i<=1;i++)for(re j=n;j>=1;j--)hc[i][1][j]=hc[i][1][j+1]*bas+ch[i][j]-'a';
	for(re i=0;i<=1;i++)for(re j=1;j<=n;j++){
		dp[i][j][1][0]=(ch[i][j]==a[1]);
		for(re k=2;k<=j;k++)
		if((k<<1)<=m)dp[i][j][k<<1][1]=(get_hc0(i,j-k+1,j)==get_ha(k+1,k<<1)&&get_hc1(i^1,j-k+1,j)==get_ha(1,k));
	}
	for(re k=1;k<=m;k++)for(re i=0;i<=1;i++)for(re j=1;j<=n;j++){
		if(ch[i][j]!=a[k])continue;
		dp[i][j][k][0]=(dp[i][j][k][0]+dp[i][j-1][k-1][0]+dp[i][j-1][k-1][1])%mod;
		dp[i][j][k][1]=(dp[i][j][k][1]+dp[i^1][j][k-1][0])%mod;
	}
	for(re i=0;i<=1;i++)for(re j=1;j<=n;j++)for(re k=0;k<=m;k++){
		if((m-k)&1)continue;
		if(m-k==2)continue;
		int mid=m-k>>1;
		if(m!=k&&(j+mid>n||get_hc0(i,j+1,j+mid)!=get_ha(k+1,k+mid)||get_hc1(i^1,j+1,j+mid)!=get_ha(k+mid+1,m)))continue;
		ans=(ans+dp[i][j][k][0]+dp[i][j][k][1])%mod;	
	}
}
signed main(){
	scanf("%s",ch[0]+1);scanf("%s",ch[1]+1);n=strlen(ch[0]+1);
	scanf("%s",a+1);m=strlen(a+1);ba[0]=1;
	for(re i=1;i<=m;i++)ba[i]=ba[i-1]*bas,ha[i]=ha[i-1]*bas+a[i]-'a';
	get_ans();
	reverse(ch[0]+1,ch[0]+n+1);
	reverse(ch[1]+1,ch[1]+n+1);
	get_ans();
	if(m==1)for(re i=0;i<=1;i++)for(re j=1;j<=n;j++)ans=(ans-(ch[i][j]==a[1])+mod)%mod;
	if(m==2)for(re i=0;i<=1;i++)for(re j=1;j<=n;j++)ans=(ans+mod-(ch[i][j]==a[1]&&ch[i^1][j]==a[2]))%mod;
	printf("%lld",ans);
}

T3 購物

這個說白了也是一個結論題,我還是不會

結論:我們在K的覆蓋軸上,可能出現空缺的點一定在i-1的字首和與\(\frac{x_i}{2}\)之間

我們證明這個結論:

我們現在不在乎i-1之前如何,我們之在乎i這個數,

\(sum_i\)為前i個數的和,\(x_i\)為第i個數,我們按照\(x_i\)從小到大排序

如果在\(sum_{i-1}\)\(\frac{x_i}{2}\)之間出現了沒有覆蓋的點,

因為後面的點都比\(x_i\)大,所以沒有點可以填補這個空缺

還要證明\(\frac{x_i}{2}-sum_i\)這個區間是連續的

首先\(sum_i=sum_{i-1}+x_i\)那麼\(\frac{sum_i}{2}\le\max(x_i,sum_{i-1})\)

所以這樣的區間一定可以接上。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e5+5;
ll n,a[N],sum[N],rec;
signed main(){
	scanf("%lld",&n);
	for(re i=1;i<=n;i++)scanf("%lld",&a[i]);
	sort(a+1,a+n+1);
	for(re i=1;i<=n;i++){
		rec+=((a[i]+1)/2-sum[i-1]-1>0?(a[i]+1)/2-sum[i-1]-1:0);
		sum[i]=sum[i-1]+a[i];
	}
	printf("%lld",sum[n]-rec);
}

T4 ants

題面翻譯:給定l,r,求[l,r]區間內的數的最長連續段

回滾莫隊大板子。。。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=100005;
int n,m;
int c[N],pos[N],di;
int ans[N];
int lb[N],rb[N];
struct query{
	int l,r,id,p;
}qus[N];
struct front{
	int typ,p,val;
}fro[N];
inline bool comp(query x,query y){
	if(pos[x.l]==pos[y.l])
		return x.r<y.r;
	return x.l<y.l;
}
int main(){
	scanf("%d%d",&n,&m);
	di=sqrt(n);
	for(re i=1;i<=n;i++){
		scanf("%d",&c[i]);
		pos[i]=i/di+1;
	}
	for(re i=1;i<=m;i++){
		scanf("%d%d",&qus[i].l,&qus[i].r);
		qus[i].id=i;
		qus[i].p=pos[qus[i].l];
	}
	sort(qus+1,qus+m+1,comp);
	int r,sum,res,tmp;
	for(re i=1;i<=m;i++){
		if(qus[i].p!=qus[i-1].p){
			sum=0;
			for(re j=1;j<=n;j++)
				lb[j]=rb[j]=0;
			r=qus[i].p*di;
		}
		while(r<qus[i].r){
			r++;
			lb[c[r]]=lb[c[r]-1]+1;
			rb[c[r]]=rb[c[r]+1]+1;
			tmp=lb[c[r]]+rb[c[r]]-1;
			sum=max(sum,tmp);
			lb[c[r]+rb[c[r]]-1]=tmp;
			rb[c[r]-lb[c[r]]+1]=tmp;
		}
		res=sum;
		int s=0;
		for(re l=qus[i].l;l<=min(qus[i].r,qus[i].p*di);l++){
			lb[c[l]]=lb[c[l]-1]+1;
			rb[c[l]]=rb[c[l]+1]+1;
			tmp=lb[c[l]]+rb[c[l]]-1;
			res=max(res,tmp);
			fro[++s].typ=1;fro[s].p=c[l]+rb[c[l]]-1;fro[s].val=lb[c[l]+rb[c[l]]-1];
			fro[++s].typ=2;fro[s].p=c[l]-lb[c[l]]+1;fro[s].val=rb[c[l]-lb[c[l]]+1];
			lb[c[l]+rb[c[l]]-1]=tmp;
			rb[c[l]-lb[c[l]]+1]=tmp;
		}
		ans[qus[i].id]=res;
		for(re j=s;j>=1;j--){
			if(fro[j].typ==1)
				lb[fro[j].p]=fro[j].val;
			else
				rb[fro[j].p]=fro[j].val;
		}
		for(re j=qus[i].l;j<=min(qus[i].r,qus[i].p*di);j++){
			lb[c[j]]=rb[c[j]]=0;
		}
	}
	for(re i=1;i<=m;i++){
		printf("%d\n",ans[i]);
	}
}