評分一覽:21-30-692-1643-2377-2323-2207-3229
只做了 DEFGH。比賽鏈接。
D. Project Planning
給定 \(N\) 個正數 \(A_i\),每次選擇 \(K\) 個並將它們減去 \(1\),求出最多能做的操作步數。
\(1\le K\le N\le 2\times 10^5\),\(1\le A_i\le 10^{12}\)。
Solution
考慮二分一個操作步數 \(s\),那么問題轉化為了,能不能做 \(s\) 步上述操作。
結論:設 \(\ge s\) 的有 \(x\) 個,\(<s\) 的數和為 \(sum\),若 \(sum\ge (K-x)\times s\),那么可以做出,否則不可以。
時間復雜度 \(O(n\log \frac{\sum A_i}{K})\)。
Code
const int N=2e5;
int n,K;
ll ans;
ll a[N+10],ma;
bool chk(ll x) {
int cnt=0;ll sum=0;
for(int i=1;i<=n;i++) {
if(a[i]>=x) cnt++;
if(a[i]<x) sum+=a[i];
}
// printf("%d %lld\n",cnt,sum);
return sum>=1ll*(K-cnt)*x;
}
int main() {
scanf("%d %d",&n,&K);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),ma+=a[i];
ll L=1,R=ma/K;
while(L<=R) {
ll mid=(L+R)>>1;
if(chk(mid)) L=mid+1;
else R=mid-1;
}
printf("%lld\n",L-1);
}
E. Swap
給定一個字符串 \(S\),其字符集為 K
,E
,Y
,求交換相鄰的兩個字符至多 \(K\) 次之后,能夠產生的字符串個數。
\(2\le |S|\le 30\),\(0\le K\le 10^9\)。
Solution
設 \(f_{S,i}\) 表示目前的字符串是 \(S\),還有 \(i\) 次操作可以使用。
因為字符集很少,我們不難考慮枚舉每一個字符,並將它提取到當前的最前面,他前面的字符則向后移一位。
不難發現相同的字符只需要枚舉到第一個就退出,因為越前面花費的就越少,而且其答案沒有本質變化。
使用記憶化搜索實現,時間復雜度約為 \(O(3^{|S|/3})\) 的樣子。
Code
map<pair<string,int>,ll> memo;
ll solve(string S,int K) {
int n=S.size();
if(K<0) return 0;
if(n<=1) return 1;
auto p=make_pair(S,K);
if(memo[p]!=0) return memo[p];
ll ret=0;
for(auto c:"KEY") for(int i=0;i<n;i++) if(S[i]==c) {
ret+=solve(S.substr(0,i)+S.substr(i+1),K-i);break;
}
return memo[p]=ret;
}
F. Treasure Hunting
有一個 \(H\times W\) 的矩陣,現在從左上角走到右下角,對前 \(K\) 大的權值求和,輸出最小的和。
\(1\le H,W\le 30\),\(1\le K<H+W\)。
Solution
考慮枚舉第 \(K+1\) 大的值 \(x\),設 \(f_{i,j,k}\) 表示現在走到了 \((i,j)\),已經有了 \(k\) 個大於 \(x\) 的數,他們的和是多少。
轉移十分簡單:當 \(A_{i,j}<x\),\(f_{i,j,k}=\min(f_{i-1,j,k}+f_{i,j-1,k})\),當 \(A_{i,j}>x\),\(f_{i,j,k}=\min(f_{i-1,j,k-1},f_{i,j-1,k-1})+A_{i,j}\),注意當 \(A_{i,j}=x\) 的時候需要跑兩遍。
時間復雜度 \(O(H^2W^2K)\)。
Code
const int N=30;
const ll inf=1e18;
int n,m,K;
int a[N+10][N+10];
ll f[N+10][N+10][N+N+10],ans=inf;
int main() {
scanf("%d %d %d",&n,&m,&K);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
for(int I=1;I<=n;I++)
for(int J=1;J<=m;J++) {
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=max(i+j-1,K);k++)
f[i][j][k]=inf;
if(a[I][J]>a[1][1]) f[1][1][0]=0;
else if(a[I][J]<a[1][1]) f[1][1][1]=a[1][1];
else f[1][1][0]=0,f[1][1][1]=a[1][1];
// printf("%d:\n",a[I][J]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(i!=1||j!=1) {
if(a[I][J]>a[i][j]) for(int k=0;k<=i+j-1;k++) f[i][j][k]=min(f[i-1][j][k],f[i][j-1][k]);
else if(a[I][J]<a[i][j]) for(int k=1;k<=i+j-1;k++) f[i][j][k]=min(f[i-1][j][k-1],f[i][j-1][k-1])+a[i][j];
else {
for(int k=0;k<=i+j-1;k++) f[i][j][k]=min(f[i-1][j][k],f[i][j-1][k]);
for(int k=1;k<=i+j-1;k++) f[i][j][k]=min(min(f[i-1][j][k-1],f[i][j-1][k-1])+a[i][j],f[i][j][k]);
}
// for(int k=1;k<=i+j-1;k++) printf("f[%d][%d][%d] is %lld\n",i,j,k,f[i][j][k]);
}
ans=min(ans,f[n][m][K]);
}
printf("%lld\n",ans);
}
G. Divisors of Binomial Coefficient
求 \(\binom{N}{K}\) 的質因子個數。
\(1\le N\le 10^{12}\),\(0\le K\le \min(10^6,N)\)。
Solution
考慮展開組合數,對上下分別求質因子個數。
不難發現大於 \(\sqrt{N}\) 的質因子可以在最后處理,那么只需要預處理 \(10^6\) 內的質數,然后對上下分別暴力分解就可以了。
時間復雜度 \(O(10^6\log K)\)。
Code
using ll=long long;
#define int long long
const int N=1e6,mod=998244353;
bool pr[N+10];
ll n;int K,ans=1;
ll up[N+10],down[N+10];
signed main() {
scanf("%lld %lld",&n,&K);
memset(pr,1,sizeof pr);
pr[0]=pr[1]=0;
for(ll i=2;i*i<=N;i++) if(pr[i])
for(ll j=i+i;j<=N;j+=i) pr[j]=0;
for(ll i=1;i<=K;i++) down[i]=i;
ll fuck=n-K+1;
for(ll i=0;i<K;i++) up[i]=fuck+i;
// for(int i=1;i<=K;i++) printf("%lld %lld\n",up[i],down[i]);
for(int p=2;p<=N;p++) if(pr[p]) {
int cnt=0;
for(ll i=p;i<=K;i+=p) while(down[i]%p==0) down[i]/=p,cnt--;
for(ll i=((n-K+1)+p-1)/p*p;i<=n;i+=p) while(up[i-fuck]%p==0) up[i-fuck]/=p,cnt++;
// printf("%d %d\n",i,cnt);
ans=ans*(cnt+1)%mod;
}
// printf("%d\n",ans);
for(ll i=n-K+1;i<=n;i++) if(up[i-fuck]!=1) ans=1ll*ans*2%mod;
printf("%lld\n",ans);
}
H. Eat Them All
有一個 \(3\times 3\) 的網格圖,現在 Snuke 在 \((1,1)\),Snuke 重復如下操作:
- 使當前位置的 \(A_{i,j}\) 減去 \(1\)。
- 走到與當前位置相鄰的某一個格子。
試構造一組方案使得 Snuke 最終可以使得 \(A_{i,j}\) 全部歸為 \(0\)。
\(1\le A_{i,j}\le 100\)。
Solution
不難發現對於每一個點,其周圍邊的經過次數之和是 \(2A_{i,j}\),可以以這個作為限制,列出一個十二元一次方程組,包括九個方程,瞎幾把 yy 會發現有四個自由元。
這些全都可以暴力枚舉,注意枚舉的時候剪枝,最后要注意建出來的圖能否連通。
然后將每一條邊,設他被經過了 \(x_i\) 次,直接拆成 \(x_i\) 條邊,不難發現這個圖一定存在歐拉回路,直接大力跑就可以了。
時間復雜度 \(O(200^4)\)。
Code
using pii=pair<int,int>;
#define se second
#define fi first
#define pb push_back
#define mp make_pair
int x[20],a[4][4];
bool used[200*200*200+10];
bool chk(int i) {
if(x[i]<0) return 1;
return 0;
}
string ans;
char turn(int x,int y) {
int xx=(x-1)/3,xy=(x-3*xx==0?3:x-3*xx);
int yx=(y-1)/3,yy=(y-3*yx==0?3:y-3*yx);
xx++,yx++;
if(xx==yx&&xy+1==yy) return 'R';
if(xx==yx&&xy-1==yy) return 'L';
if(xy==yy&&xx+1==yx) return 'D';
if(xy==yy&&xx-1==yx) return 'U';
return '*';
}
struct UFS {
int fa[10];
void init() {for(int i=1;i<=9;i++) fa[i]=i;}
int find(int x) {if(x==fa[x]) return x;return fa[x]=find(fa[x]);}
void merge(int x,int y) {int fx=find(x),fy=find(y);if(fx==fy) return;fa[fx]=fy;}
} U;
int cnt=0;
vector<pii> G[10];
int st[10];
vector<int> path;
void connect(int u,int v,int I) {
for(int i=1;i<=I;i++) G[u].pb(mp(v,i+cnt)),G[v].pb(mp(u,i+cnt));
cnt+=I;
}
void dfs(int u) {
// printf("visit %d\n",u);
// for(auto v:G[u]) printf("%d %d\n",v.fi,v.se);
for(int i=st[u];i<(int)G[u].size();i=max(i+1,st[u]))
if(!used[G[u][i].se]) {
used[G[u][i].se]=1;st[u]=i+1;
dfs(G[u][i].fi);
}
path.pb(u);
}
signed main() {
for(int i=1;i<=3;i++)
for(int j=1;j<=3;j++)
scanf("%d",&a[i][j]);
for(x[1]=0;x[1]<=200;x[1]++) {
x[7]=2*a[1][1]-x[1];
if(chk(7)) continue;
for(x[2]=0;x[2]<=200;x[2]++) {
x[8]=2*a[1][2]-x[1]-x[2];
if(chk(8)) continue;
x[9]=2*a[1][3]-x[2];
if(chk(9)) continue;
for(x[5]=0;x[5]<=200;x[5]++) {
x[10]=2*a[3][1]-x[5];
if(chk(10)) continue;
x[3]=2*a[2][1]-x[7]-x[10];
if(chk(3)) continue;
for(x[6]=0;x[6]<=200;x[6]++) {
x[11]=2*a[3][2]-x[5]-x[6];
if(chk(11)) continue;
x[12]=2*a[3][3]-x[6];
if(chk(12)) continue;
x[4]=2*a[2][3]-x[9]-x[12];
if(chk(4)) continue;
if(x[3]+x[4]+x[11]+x[8]!=2*a[2][2]) continue;
U.init();
if(x[1]) U.merge(1,2);
if(x[2]) U.merge(2,3);
if(x[3]) U.merge(4,5);
if(x[4]) U.merge(5,6);
if(x[5]) U.merge(7,8);
if(x[6]) U.merge(8,9);
if(x[7]) U.merge(1,4);
if(x[8]) U.merge(2,5);
if(x[9]) U.merge(3,6);
if(x[10]) U.merge(4,7);
if(x[11]) U.merge(5,8);
if(x[12]) U.merge(6,9);
bool fl=0;
for(int i=1;i<=9;i++) {
for(int j=1;j<=9;j++)
if(U.find(i)!=U.find(j)) {
fl=1;break;
}
if(fl) break;
}
if(fl) continue;
connect(1,2,x[1]),connect(2,3,x[2]),connect(4,5,x[3]),connect(5,6,x[4]);
connect(7,8,x[5]),connect(8,9,x[6]),connect(1,4,x[7]),connect(2,5,x[8]);
connect(3,6,x[9]),connect(4,7,x[10]),connect(5,8,x[11]),connect(6,9,x[12]);
// for(int i=1;i<=12;i++) printf("%d ",x[i]);puts("");
// for(int i=1;i<=9;i++) {
// // printf("%d:\n",i);
// for(auto v:G[i]) printf("%d %d\n",i,v.fi);
// }
dfs(1),reverse(path.begin(),path.end());
// for(auto i:path) printf("%d ",i);puts("");
for(int i=1;i<(int)path.size();i++) ans+=turn(path[i-1],path[i]);
cout<<ans<<endl;
return 0;
}
}
}
}
puts("NO");
}