P6104 [EER2]相同的數字:貪心+線性篩+模擬+後綴和

題目來源: P6104 [EER2]相同的數字.
題目大意:對一串固定的數字,可以每次將數字+1花費c1,可以將數字變爲比他大的最小的質數,花費c2,最終要將所有數字變得一樣。問最小花費是多少?
這個題目思路自己想的,過了樣例後0分,又參考題解修改了寫小細節。
因爲題目給的變化是隻能是變大,所以將數字排序,每個數字變成其中最大的數字a[n],或者比a[n]大的最小質數。
怎麼變化呢?要麼一步一步跳,要麼質數的間距跳,設質數的間距t,打了個1e7的表看了下,最大間距只有154,可以用桶來計數.間距越大,那麼用c2跳越合算,如果c1*t>c2;那麼就用質數的方法跳;可以得到結論:t>c2/c1,那麼用的c2方法,否則用c1方法。將所有質數跳的次數和跳的距離維護後綴和,然後根據c2/c1的計算找到對應的位置,o(1)即可計算。
所以問題就變爲如何統計這些數在質數上的跳躍次數和+1跳躍次數。這裏的細節比較多。
因爲n,q都比較大,所以應該預處理n,然後再線性處理q。


#include<cstdio>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e7 + 160;
const int mod = (1<<17);
 
bool isnp[MAXN];
int p[MAXN],pcnt=0;
ll sump[MAXN];//sump[]存儲到當前數爲值出現的質數的個數,其下一個質數就是p[sum[]+1]。 
ll n,q,T,a[MAXN],c[2][200],maxa,maxp,ans,lans=0,sumstep[2],behc[2][200],behs[2][200]; 
 
void prim(int N) {
	isnp[0]=isnp[1]=1;
	for(int i=2; i<N; ++i) {
		if(!isnp[i]) {
			p[++pcnt]=i;			
		}
		sump[i]=pcnt;
		for(int j=1; j<=pcnt; ++j) {
			if((ll)i*p[j]>=N) break;
			isnp[i*p[j]]=1;
			if(i%p[j]==0) 	break;
		}
	}	
}
//求這些數到終點x的的值終點有兩種可能,到n;如果n不是質數,到n對應的下一個質數 
void count(int flag,int x){//計算出:1.需要跳多少個1步sumstep;2桶計數c[]:跳到對應的質數間距計數 
	sumstep[flag]=0;
	if(flag==0)//這些數要跳到一個合數,只能+1來跳。 
		sumstep[flag]+=(n-1)*(a[n]-p[sump[a[n]]]),a[n]=p[sump[a[n]]];
	else if(isnp[a[n]]){//可以通過+1跳到這個質數,也可以通過下一個質數跳到。
		sumstep[flag]+=p[sump[a[n]]+1]-a[n];
		c[flag][sumstep[flag]]++;
		a[n]=p[sump[a[n]]+1];
	}
	int j=sump[a[n]];
	for(int i=n;i>1;i--){
		sumstep[flag]+=(a[n]-a[i-1]); //+1來挑 
		while(p[j]>a[i-1]&&p[j-1]>=a[i-1]&&j){
			c[flag][p[j]-p[j-1]]+=(i-1);//從質數跳到質數 
			j--;
		}
		if(isnp[a[i-1]])c[flag][p[j]-a[i-1]]++; //第一步可能是合數跳到質數	
	}
	//因爲質數跳的距離越大用c2越合算,維護c[t]的後綴和,和所代替步數t*c[t]的後綴和 
	for(int i=161;i>=1;i--){
	
		behc[flag][i]=behc[flag][i+1]+c[flag][i];
		behs[flag][i]=behs[flag][i+1]+i*c[flag][i];
	}
	a[n]=x;	
} 
ll work(ll c1,ll c2){

	c1=c1^(T*lans),c2=c2^(T*lans); 
	ll t=ceil(1.0*c2/c1);
	if(t>160)t=160;
	ll sum0=(sumstep[0]-behs[0][t])*c1+behc[0][t]*c2;	
	ll sum1=(sumstep[1]-behs[1][t])*c1+behc[1][t]*c2;
	return min(sum0,sum1);	
	
} 
int main(void) {
//	freopen("in.txt","r",stdin);
	ll c1,c2;
	scanf("%lld%lld%lld",&n,&q,&T);
	for(int i=1;i<=n;i++) 
		scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
	prim(a[n]+158) ;
	count(0,a[n]);
	count(1,a[n]);
	for(int i=1;i<=q;i++){
		scanf("%lld%lld",&c1,&c2);
		ans=work(c1,c2);
		lans=ans%mod;
		printf("%lld\n",ans);
	}	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章