1. 程式人生 > 實用技巧 >COCI20162017 Contest#6 F

COCI20162017 Contest#6 F

COCI20162017 Contest#6 F

其實這個題不是很難的。。。

設值域為\(M\)

考慮如果沒有幸運數的限制,那麼從\(A\)變成\(B\),實際上只與\(\frac{A}{B}\)有關

不妨令\(dp_{i,j}\)為從\(i\)走了\(j\)步變成1,顯然這個\(j\)的最大值為\(\log M=19\),即\(2^{19}\)最多操作19次

\(i\)列舉倍數進行轉移,同時也暴力處理每個數的因數個數,複雜度為\(O(M\ln M\log M)\)

\[\ \]

接下來考慮幸運數的限制

推論: 最多隻會在一個幸運數上停留

如果經過多個,顯然在代價最小的那個上面停留

因此考慮列舉停留的幸運數\(x\)

那麼轉移可以分為兩步\(\frac{A}{x}\)\(\frac{x}{B}\),可以暴力合併兩個\(dp\)陣列,單次查詢複雜度為\(O(T\cdot \log^2 M)\)

合併得到的結果,可以描述為:

可以在\(x\)上用\(C(x)\)的代價停留,並且其他部分的轉移花了\(j\)的時間,\(y\)的代價

如果考慮停留的時間,那麼得到的答案顯然是一條直線,斜率就是停留的代價

關於一群直線,一群查詢,不難想到可以斜率優化求解,這一部分複雜度為\(O(T\log M\log (T\log M)+m)\)(排序複雜度)

總複雜度可以認為就是\(O(M\log^2 M+Q(T\log ^2 M+m))\)

斜率優化的實現可以參考程式碼

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize(2)
typedef long long ll;
#define reg register
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,const T &b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,const T &b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e6+10,INF=1e9;

int n,m;
int F[N],A[N],B[N];
int L[N];
// A: 因子個數
// B: 將B變成1需要的最大步數
int C[N],D[N];
int dp[N][20];
// 用了j步,將i變為1的最小代價

struct Node{ 
    // 描述一條直線
	ll x,y;
	// 答案為 x*i+y
	ll operator [](const ll i)const {
		return i*x+y;
	} //求直線點值
	bool operator < (const Node __) const {
		if(x!=__.x) return x<__.x;
		return y<__.y; //按照斜率排序
	}
} U[N];

int Uc,T[21],R[21];

int main(){
	rep(i,1,N-1) {
		A[i]++;
		for(reg int j=i+i;j<N;j+=i) A[j]++,cmax(B[j],B[i]+1);
	}
	rep(i,1,rd()) F[i]=rd();
	rep(i,1,m=rd()) L[i]=rd();
	sort(L+1,L+m+1);
	rep(i,1,N-1) rep(j,0,B[i]) dp[i][j]=INF;
	dp[1][0]=0;
	rep(i,1,N-1) rep(j,0,B[i]) if(dp[i][j]<INF) for(reg int k=i+i;k<N;k+=i) cmin(dp[k][j+1],dp[i][j]+F[A[k/i]]);
	rep(i,1,n=rd()) C[i]=rd(),D[i]=rd();

	rep(kase,1,rd()) {
		int x=rd(),y=rd(),d=x/y;
		if(x%y!=0){
			printf("%d\n",-m);
			continue;
		}
		memset(R,63,sizeof R);
		Uc=0;
		rep(i,1,n) if(x%C[i]==0 && C[i]%y==0){
			int dx=x/C[i],dy=C[i]/y;
			memset(T,63,sizeof T);
			rep(a,0,B[dx]) if(dp[dx][a]<INF) rep(b,0,B[dy]) cmin(T[a+b],dp[dx][a]+dp[dy][b]);
			rep(j,0,B[dx]+B[dy]) if(T[j]<INF) {
				ll a=D[i],b=T[j]-a*j;
				U[++Uc]=(Node){a,b};
				rep(k,j,B[d]) cmin(R[k],(int)U[Uc][k]);
			}
		}
		rep(i,0,B[d]) cmin(R[i],dp[d][i]);
		ll ans=0;
		ll mi=1e18;
		sort(U+1,U+Uc+1);
		int R=0;
		rep(i,1,Uc) {
			if(mi<U[i].y) continue;
			mi=U[i].y;
			while(R>1 && (U[i].y-U[R].y)*(U[R].x-U[R-1].x)<=(U[R].y-U[R-1].y)*(U[i].x-U[R].x)) R--;
			U[++R]=U[i]; // 單調棧處理凸包,注意加入時滿足x遞增,y遞減
		}
		rep(i,1,m) if(L[i]<=B[d]) ans+=::R[L[i]]<INF?::R[L[i]]:-1;
		else {
			while(R>1 && U[R-1][L[i]]<=U[R][L[i]]) R--;
			if(!R) ans--;
			else ans+=U[R][L[i]];
		}
		printf("%lld\n",ans);
	}
}