2018.10.6模拟赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/82955242

(本来说着要早睡结果还是到了这会···所以就简单写一下)

T1

考场写了个n*k^2  (结果评测机不知道出了什么锅少了我60分QAQ

正解应该是n*k的,就是每次+1操作用一个tag记录

然后加x就看成加x-tag,二项式定理计算每次x+tag的k次方和

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxm 55
#define LL long long
using namespace std;
int n,k,C[maxm][maxm],tag;
LL sum[maxm];
const int mod=1e9+7;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}

inline void pre(){
	C[0][0]=1;
	for(int i=1;i<=k;i++)
		for(int j=0;j<=i;j++){
			C[i][0]=1;
			if(j>0) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
		}
}

inline void insert(int x){
	LL ret=1; sum[0]++;
	for(int i=1;i<=k;i++)
		(ret*=x)%=mod,(sum[i]+=ret)%=mod;
}

inline int query(){
	LL ans=0,ret=1;
	for(int i=0;i<=k;i++){
		(ans+=ret*C[k][k-i]%mod*sum[k-i]%mod)%=mod;
		(ret*=tag)%=mod;
//		cout<<ans<<" "<<k-i<<" "<<sum[k-i]<<endl;
	}
	ans=(ans%mod+mod)%mod;
	return ans;
}

int main(){
	freopen("set.in","r",stdin);
	freopen("set.out","w",stdout);
	n=rd(); k=rd();
	pre();
	for(int i=1;i<=n;i++){
		int opt=rd();
		if(!opt){
			int x=rd();
			insert((x-tag)%mod);
		}
		else tag++;
		printf("%d\n",query());
	}
	return 0;
}

T2

也算是结论题吧

首先考虑用一些数来构造出[k,2k]的数,可以分类讨论:

>2k,删去

>=k且<=2k,直接取

<k,将这些数加起来如果>2k就一定可以构造出(挺显然的

那么考虑用a[i]可以构造出那些k?

一个a[i]可以将a[i]/2~a[i]这段区间覆盖掉,那么将a[i]加上一些小的a[i]一定会将区间右端点增大

所以就排个序然后记录1~i-1的sum然后答案是[a[i]/2,sum]的并

注意是上取整

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
#define LL long long
using namespace std;
int n,a[maxn];
LL last,ans,sum;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}

int main(){
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	n=rd();
	for(int i=1;i<=n;i++) a[i]=rd();
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++){
		int now=a[i];
		sum+=now;
		while(a[i+1]==now) sum+=now,i++;
		ans+=sum-max(last,1LL*(now+1)/2-1);
		last=sum;
	}
	printf("%lld\n",ans);
	return 0;
}

T3

毒瘤的题

居然能转化成背包问题

找找规律可以发现可行解是跟2的a[i]次方有关的

大概就是:11、2222、···每进行一轮就分成两个集合删掉一个然后剩下都减1,这些可以看成分数形式,每次都会多一倍

然后可以证明当sum>=1时,一定能分成两个集合,使s1>=0.5,s2>=0.5,这样最终留下的集合一定>=0.5

减1后sum>=1,而sum<1一定不会转移到sum>=1

当a=0,sum=1时胜利

于是就变成:分成两个集合使其都>=0.5的方案数----> 总方案数减去使其中一个<0.5 的方案数*2

就变成了背包问题

但是过程中背包容量还会变,因为后面的1只是前面的1/2

所以每次维护背包容量就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 2005
#define LL long long
using namespace std;
int T,n,a[maxn];
LL f[maxn];
const int mod=1e9+7;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}

inline LL qpow(LL x,int k){
	LL ret=1;
	while(k){
		if(k&1) ret=ret*x%mod;
		(x*=x)%=mod; k>>=1;
	} return ret;
}

inline bool cmp(int x,int y) {return x>y;}
inline void solve(){
	f[0]=1;
	sort(a+1,a+n+1,cmp);//从大到小排序 
	LL pre=a[1],tmp=0;
	for(int i=1;i<=n;i++){
		LL times=1;//翻了几倍 
		while(pre!=a[i]){
			times<<=1LL,--pre;
			if(times>tmp){
				pre=a[i]; break;
			}
		}
		if(times>1){
			int m=tmp/times;//背包容量 
			for(int j=0;j<=m;j++){
				LL res=0;
				for(int k=j*times,mk=min(k+times-1,tmp);k<=mk;k++){
					(res+=f[k])%=mod;
					f[k]=0; 
				}
				f[j]=res;
			}
			tmp/=times;
		}
		for(int j=tmp;j>=0;j--)
			(f[j+1]+=f[j])%=mod;
		++tmp;//在这个地方有几个僵尸 
	}
	LL t=1,ans=0;
	while(pre>1 && t<tmp){
		--pre;
		t=min(t<<1,tmp);
	}
	for(int i=0;i<t;i++) (ans+=f[i])%=mod;
	printf("%lld\n",(qpow(2,n)-(ans<<1)%mod+mod)%mod);//总的-不合法的 
}

int main(){
	freopen("pvz.in","r",stdin);
	freopen("pvz.out","w",stdout); 
	T=rd();
	while(T--){
		n=rd(); memset(f,0,sizeof f);
		for(int i=1;i<=n;i++) a[i]=rd();
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/82955242
今日推荐