【2019.10.25】
summary
- 分情况拿分保底真的很好用 像我这种辣鸡应该注意保底
- 打题不要慌 有条理 不要东一条西一条
小奇采药
对于 30% 的数据,O(2n ) 枚举取 or 不取
对于 60% 的数据,O(nm) 做 01 背包,即 f(i, j) 表示前 i 株 草药,耗费 j 的时间能达到的最⼤代价。
对于 100% 的数据,注意到 m,t,v 纯随机
那么不会选太多的草药,⽽耗时较少的草药有很⼤概率存在 于最优解中
针对这些性质优化搜索
当然也可以合理使用随机化和卡时,复杂度 O(⽞学)
最基础60昏的背包
dfs写错了... 幸好我分开搞得 不然就爆炸
struct node{int co,val;}a[N];
bool cmp(node x,node y){return x.co>y.co;}
ll f[100010],ans,sumc[N],sumv[N];
void work(){//60昏
memset(f,0,sizeof(f));
for(int i=1;i<=n;++i)
for(int j=m;j>=a[i].co;--j)
f[j]=max(f[j],f[j-a[i].co]+a[i].val);
printf("%lld\n",f[m]);
}
bool vis[N];
void dfs(int pos,ll co,ll val){
ans=Max(ans,val);
if(pos>n) return;
if(co+a[n].co>m) return;
if(val+sumv[pos]<=ans) return;
if(co+sumc[pos]<=m){ans=Max(val+sumv[pos],ans);return;}
if(co+a[pos].co<=m) dfs(pos+1,co+a[pos].co,val+a[pos].val);
dfs(pos+1,co,val);
}
void work2(){
ans=0;dfs(1,0,0);
printf("%lld\n",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int T;rd(T);
while(T--){
rd(n),rd(m),sumv[n+1]=sumc[n+1]=0;
for(int i=1;i<=n;++i) rd(a[i].co),rd(a[i].val);
sort(a+1,a+n+1,cmp);
for(int i=n;i;--i) sumv[i]=sumv[i+1]+a[i].val,sumc[i]=sumc[i+1]+a[i].co;
//if(m<=100000) work();
work2();
}
return 0;
}
小奇的数列2
第一反应 双指针?!
很容易发现 若一个区间为可约的 那么这个区间的gcd一定等于其最小值
然后发现可以二分 就开始打线段树来维护 打完后发现二分的过程中有很多此询问 gcd和最小值都可以用st表来维护 然后\(O(1)\)询问最合适了!
然后这就是正解辽?!
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
int gc,mn;
void query(int x,int y){
int s=lg[y-x+1];
gc=gcd(Gc[x][s],Gc[y-(1<<s)+1][s]),mn=Min(Mn[x][s],Mn[y-(1<<s)+1][s]);
}
bool check(int mid){
for(int l=1;l+mid<=n;++l)
if(query(l,l+mid),gc==mn) return 1;
return 0;
}
int main(){
//freopen("sequence.in","r",stdin);
// freopen("sequence.out","w",stdout);
rd(n);
for(int i=1;i<=n;++i) rd(a[i]);
cm[0]=1,lg[0]=-1;for(int i=1;i<20;++i) cm[i]=cm[i-1]<<1;
for(int i=1;i<=n;++i) Gc[i][0]=Mn[i][0]=a[i],lg[i]=lg[i>>1]+1;Mn[n+1][0]=0;
for(int j=1;j<20;++j)
for(int i=1;i+cm[j]-1<=n+1;++i){
Gc[i][j]=((Gc[i][j-1]==1||Gc[i+cm[j-1]][j-1]==1)?1:gcd(Gc[i][j-1],Gc[i+cm[j-1]][j-1])),
Mn[i][j]=Min(Mn[i][j-1],Mn[i+cm[j-1]][j-1]);
}
int l=0,r=n-1,mid,ans,cnt=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
if(!ans){
printf("%d %d\n",n,0);
for(int i=1;i<=n;++i) printf("%d ",i);
}
else{
for(int i=1;i+ans<=n;++i)
if(query(i,i+ans),gc==mn) b[++cnt]=i;
printf("%d %d\n",cnt,ans);
for(int i=1;i<=cnt;++i) printf("%d ",b[i]);
}
return 0;
}
小奇学数论
我... 表打炸了...
对于 20% 的数据,枚举 1 到 n,暴⼒判断素数。
对于 40% 的数据,枚举 1 到 n,判断素数的时候只需要循 环 1 − √ n
对于 60% 的数据,筛法
对于 80% 的数据,容斥原理
对于 100% 的数据,分段 + 前缀和打表 还有个⿊科技 Meissel-Lehmer 算法
分块打表 打表时用的是做素数密度那道题的方法==
int cnt=0,prime[700000];bool v[N];
void primes(){
for(ll i=2;i<=n;++i){
if(!v[i]) v[i]=1,prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;++j){
v[i*prime[j]]=1;
if(!(i%prime[j])) break;
}
}
}
bool a[5000010];
void work(){
m=n,n=1000000ll;primes();
for(int i=1;i<=m/5000000;++i) ans+=table[i];
ll L=(m/5000000ll+1)*5000000ll,R=m;
if(!(m%5000000ll)) {printf("%lld\n",ans);return;}
memset(a, 0, sizeof(a));
for (int i=1;i<=cnt;++i)
for(ll j=max(2ll,(L-1)/prime[i]+1)*prime[i];j<=R;j+=prime[i]) a[j-L]=1;
for(ll i=L;i<=R;++i)
if(!a[i - L]) ++ans;
printf("%lld",ans);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
rd(n);
if(n<=10000000){
primes();
printf("%d",cnt);
}
else work();
/* int i=1;
for(;i<=20000;++i) if(table[i]==2) break;
printf("%d",i);*/
return 0;
}