(本来说着要早睡结果还是到了这会···所以就简单写一下)
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;
}