【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
",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
",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,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
",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
",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;
}
原文地址:https://www.cnblogs.com/lxyyyy/p/11738487.html