題目來源: 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);
}
}