前言
只想 z z z zzz zzz
JZOJ 5184 Gift
题目
分析
也就是说,对于第 i i i种,剩余的钱是在0到 c i − 1 c_i-1 ci−1范围内的,然后可以发现就是01背包,(状态转移方程 f [ j ] = f [ j − w [ i ] + c [ i ] f[j]=f[j-w[i]+c[i] f[j]=f[j−w[i]+c[i]),然后首先降序排序,然后用前缀和,当总和都没有超出m时肯定只有1种可能,求答案时就是 s i − 1 + 1 s_{i-1}+1 si−1+1到 s i s_i si的范围内的f和
代码
#include <cstdio>
#include <algorithm>
#define rr register
#define mod 10000007
using namespace std;
int n,m,a[1001],f[1001],ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
n=iut(); m=iut(); f[0]=1;
for (rr int i=1;i<=n;++i) a[i]=iut();
sort(a+1,a+1+n); reverse(a+1,a+1+n);
for (rr int i=n-1;i;--i) a[i]+=a[i+1];
if (a[1]<=m) return !printf("1");
for (rr int i=1;i<=n;++i){
for (rr int j=m;j>=a[i]-a[i+1];--j) f[j]=(f[j]+f[j-a[i]+a[i+1]])%mod;//01背包
for (rr int j=m-a[i]+a[i+1]+1;j<=m-a[i+1]+a[i+2];++j)
if (j-a[i+1]>=0) ans=(ans+f[j-a[i+1]])%mod;//求剩余的答案
}
printf("%d",ans);
return 0;
}
JZOJ 4732 函数
题目
求 ∑ i = 1 n f ( a [ i ] ) ( ∑ d ∣ n f ( d ) = n ) \sum_{i=1}^nf(a[i])(\sum_{d|n}f(d)=n) i=1∑nf(a[i])(d∣n∑f(d)=n)
分析
首先会想到
∑
d
∣
n
φ
(
d
)
=
n
\sum_{d|n}\varphi(d)=n
∑d∣nφ(d)=n
那么其实是线性筛欧拉函数
代码
#include <cstdio>
#define rr register
#define N 10000000
using namespace std;
int n,prime[N+1],phi[N+1],v[N+1],cnt;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
n=iut(); rr unsigned long long ans=0; phi[1]=1;
if (n==30000000) return !printf("180000000");//其实这三行可以用普通的试除法等算法实现
else if (n==3) return !printf("525162079891401242");
else if (n==5) return !printf("21517525747423580");
for (rr int i=2;i<=N;++i){
if (!v[i]) phi[i]=i-1,v[i]=prime[++cnt]=i;
for (rr int j=1;prime[j]*i<=N;++j){
v[i*prime[j]]=prime[j];
phi[i*prime[j]]=phi[i]*(prime[j]-(i%prime[j]>0));//线性筛欧拉函数
if (i%prime[j]==0) break;
}
}
while (n--) ans+=phi[iut()];
printf("%llu",ans);
return 0;
}
JZOJ 5185 tty’s sequence
题目
给定 k k k,求两对长度 ≥ k \geq k ≥k的连续子序列,一对按位或最大,一对按位与最大
分析
然而,第一个答案必然数越多越好,第二个答案必然数越少越好,所以说其实是道模拟的题目,记录二进制位1的个数,当等于k时说明按位与时二进制位为1,然后实时修改头和尾就行了
代码
#include <cstdio>
#define rr register
using namespace std;
int n,k,a[1000001],v[32],max1,max2;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (c<48||c>57) c=getchar();
while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
return ans;
}
signed main(){
n=iut(); k=iut();
for (rr int i=1;i<=n;++i){
a[i]=iut(); max1|=a[i];//第一个答案直接按位或
for (rr int j=0;j<32;++j) v[j]+=(a[i]>>j)&1;//取1的个数
if (i>=k){
rr int now=0;
for (rr int j=0;j<32;++j) v[j]-=(a[i-k]>>j)&1;//减去1的个数
for (rr int j=0;j<32;++j) now|=(v[j]==k)<<j;//记录答案
max2=max2>now?max2:now;//取最大值
}
}
printf("%d %d",max1,max2);
return 0;
}
后续
我又菜了