T1 東方記者
簡單 DP,設 \(dp_{i,j}\) 表示最後走到了第 \(i\) 個事件的發生地點,且一共收集了 \(j\) 處資料的最小移動距離。
根據定義,可以知道對於所有 \(dp_{i,j}\),如果走回原點後,依然滿足移動距離小於 \(d\),則有:\(ans=max\{j\}\)。
狀態轉移也很簡單:\(dp_{i,j}=min_{k=0}^{k<i}\{dp_{k,j-1}+\lvert x_i-x_k\rvert+\lvert y_i-y_k\rvert\}\),初值 \(dp_{0,0}=x_0=y_0=0\)。
不作過多贅述,程式碼如下(100pts):
#define N 105
long long n,d,ans,x[N],y[N],dp[N][N];
int main(){
memset(dp,0x3f,sizeof dp),dp[0][0]=0,scanf("%lld",&n);
for(int i=1;i<=n;i++) dp[i][0]=0,scanf("%lld%lld",x+i,y+i);
scanf("%lld",&d);
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++)
for(int k=1;k<=j+1;k++)
dp[i][k]=min(dp[i][k],dp[j][k-1]+abs(x[i]-x[j])+abs(y[i]-y[j]));
for(int j=ans;j<=i;j++) if(dp[i][j]+abs(x[i])+abs(y[i])<=d) ans=j;
}
printf("%lld\n",ans);
}
T2 金鑰破解
先說 30pts 的做法:
先透過試除法給 \(N\) 分解質因數求出 \(p\) 和 \(q\),然後就能求出 \(r\)。
\(d\) 就是 \(e\) 在模 \(r\) 意義下的逆元,用 exgcd 求出就行了,最後 \(n\) 快速冪計算 \(c^d \mod N\) 的值就可以了。
100pts 的做法只是在此基礎上把分解質因數的方法換成了 Pollard Rho,並且因為 \(N\) 比較大,需要龜速乘或者 __int128 進行計算。
程式碼如下,思路其實很簡單,主要難度在 Pollard Rho(100pts):
#define LL long long
LL e,n,c,r;
LL quick_mul(LL x,LL y,LL mod)//x和y的積對mod取模
LL quick_pow(LL x,LL y)//x的y次方對n取模
void exgcd(LL a,LL b,LL &x,LL &y){
if(!b) return x=1,y=0,void();
exgcd(b,a%b,y,x),y=(y-quick_mul(a/b,x,r)+r)%r;
}
mt19937 mrand(time(NULL));
LL gcd(LL x,LL y){return !y?x:gcd(y,x%y);}
LL pollard_rho(LL c){
LL i=0,k=2,x=mrand()*mrand()%(n-1)+1,y=x,d;
while(1){
x=(quick_mul(x,x,n)+c)%n,d=gcd((x-y+n)%n,n);
if(d!=1&&d!=n) return d;
if(x==y) return n;
if(++i==k) k<<=1,y=x;
}
}
int main(){
scanf("%lld%lld%lld",&e,&n,&c);
LL p=n,d,tmp;
while(p==n) p=pollard_rho(mrand()*1ll*mrand()%(n-1)+1);
r=(p-1)*(n/p-1),exgcd(e,r,d,tmp);
return printf("%lld %lld\n",d,quick_pow(c,d)),0;
}
T3 琪露諾數
經典數位 DP,但是是高精度版本。
對於數位 DP 列舉到的這一位,如果是迴文串的前半段,就正常列舉就行。
如果是後半段就不用列舉了,直接判斷對應位的取值在這一位能否取到,能就繼續遞迴,不能就直接返回 0,遞迴到末尾時返回 1。
有個小技巧就是在後半段時如果 \(limit\) 已經為 0,也就是可以任意取值的時候,可以直接返回 1。
至於如何判斷是在前後哪一個半段,記搜的時候加一個值 \(st\) 表示是從哪一位開始不是前導 0 的,也就是記錄數的位數。
本題的主要難點不在於數位 DP,而是高精度的加減乘除,程式碼放下面,高精度部分不做贅述(100pts):
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL t,num[205],s[205];
string l,r,dp[205][205];
string add(string a,string b)//高精度十進位制整數a+b
string sub(string a,string b)//高精度十進位制整數a-b
string mul(string a,string b)//高精度十進位制整數a*b
string div(string a,string b,string res="")//高精度十進位制整數a/b(向下取整)
string dfs(int pos,int st,bool limit){
if(!pos) return "1";
if(st!=-1&&!limit&&dp[pos][st]!="") return dp[pos][st];
if(st-pos>=pos){
if(!limit) return "1";
if(num[pos]<s[st-pos+1]) return "0";
return dfs(pos-1,st,s[st-pos+1]==num[pos]);
}
string res="0";
for(int i=0;i<=(limit?num[pos]:8);i++){
s[pos]=i;
if(!i&&st==-1) res=add(res,dfs(pos-1,st,limit&&i==num[pos]));
else if(st==-1) res=add(res,dfs(pos-1,pos,limit&&i==num[pos]));
else res=add(res,dfs(pos-1,st,limit&&i==num[pos]));
}
if(st!=-1&&!limit) dp[pos][st]=res;
return res;
}
string solve(string x,int pos=0){
if(x=="0") return "1";
while(x!="0"){
string tmp=div(x,"9");
num[++pos]=sub(x,mul(tmp,"9"))[0]-'0',x=tmp;
}
return dfs(pos,-1,1);
}
int main(){
cin>>t;
while(t--){
cin>>l>>r;
cout<<sub(solve(r),solve(sub(l,"1")))<<"\n";
}
}