河北工业大学 ACM 集训队 2023 年夏季选拔 题解 12/12
https://ac.nowcoder.com/acm/contest/59007
A
假设数字n有len位
则小len的长度,每个都有九个方案。
长度和len一样的,至少有n[0]-1种方案
n[0]n[0]n[0]...的这个方案暴力地跑一遍看看是不是小于等于n即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } char s[1000]; int len,ans; int main() { // freopen("1.in","r",stdin); scanf("%s",s+1); len=strlen(s+1); ans=len*9-9; ans++; for(int i=2;i<=len;i++) if(s[i]<s[1]) { ans--; break; } ans+=s[1]-'1'; cout<<ans; }
B
赛场上找规律找出来的,发现是n*n*k的因子数量-1。
考虑把n和k质因子分解,答案是∏(因子数量+1)
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } ll n,k,ans; map<int,int>o; void work() { n=read();k=read();ans=1;o.clear(); for(int i=2;i*i<=n;i++) while(n%i==0) { o[i]+=2; n=n/i; } if(n!=1) o[n]+=2; for(int i=2;i*i<=k;i++) while(k%i==0) { o[i]++; k=k/i; } if(k!=1) o[k]++; for(auto x:o) ans=ans*(x.second+1); printf("%lld\n",ans-1); } int main() { for(ll t=read();t;t--) work(); }
赛后请数论大师@chdy做了做
C
输出RUSHB,连我个圈外人都知道
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } int main() { // freopen("1.in","r",stdin); cout<<"RUSHB"; }
D
注意到n只有8,所以暴力跑n的排列,按题意模拟即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } double ans,sum; int w0,w[110],x[110],y[110],noww,n,a[110]; double askdis(int u,int v) { return sqrt((x[u]-x[v])*(x[u]-x[v])+(y[u]-y[v])*(y[u]-y[v])); } void dfs(int d) { if(d==n+1) { sum=0;noww=w0; for(int i=1;i<=n;i++) { sum=sum+askdis(a[i],a[i-1])*noww; noww=noww+w[a[i]]; } ans=min(ans,sum); return ; } for(int i=d;i<=n;i++) { swap(a[i],a[d]); dfs(d+1); swap(a[i],a[d]); } } int main() { ans=1e18; n=read();w0=read(); for(int i=1;i<=n;i++) x[i]=read(); for(int i=1;i<=n;i++) y[i]=read(); for(int i=1;i<=n;i++) { w[i]=read(); a[i]=i; } dfs(1); printf("%.7lf",ans); }
E
注意到k只有50,m只有1000,所以mk地模拟一下即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } ll a[1010]; int k,m,c[60],mod=998244353; int main() { k=read();m=read(); for(int i=1;i<=k;i++) c[i]=read(); for(int i=1;i<=k;i++) a[i]=read(); for(int i=k+1;i<=m;i++) for(int j=1;j<=k;j++) a[i]=(a[i]+c[j]*a[i-j])%mod; cout<<a[m]; }
F
经典前缀和。考虑把+看成1,-看成-1,区间[l,r]“正负相互抵消”等价于l-1和r的前缀和相等。
所以记录一下每个前缀和第一次出现的位置,ans=max(每个前缀和最后一次出现的位置-第一次出现的位置)
代码方面可以用+1000000的方式把负数前缀和转移到正数里,然后用数组存。或者像我一样使用map存第一次出现的位置。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } char s[1000010]; map<int,int>f; int sum,n,ans; int main() { f[0]=0; scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++) { if(s[i]=='+') sum++; else sum--; if(f.count(sum)) ans=max(ans,i-f[sum]); else f[sum]=i; } cout<<ans; }
G
问题是说给定一个对称串,能否重新排列使得变成另一个对称串。
那么对称的两个字母必须一起行动,如果n是奇数,中间的字母不影响答案。
所以std统计一下对称的字母的种类数,如果等于1就不能,对应着aaaaaa或者aabaa这些情况。
如果大于1就可以,此时把任意两个不相同的字母交换一下即可。
总之,std是对的,输出yes(话说我去cf交一下代码不就知道对不对了)
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } int main() { // freopen("1.in","r",stdin); for(int t=read();t;t--) cout<<"YES\n"; }
H
出题人生怕不会写,甚至把判断凸包包含给端上来了,并且点的坐标都是整数。
考虑暴力枚举i,j,判断如果一个在另一个里面就连有向边,于是会连出来一个有向无环图。
对有向无环图跑拓扑排序,计算深度d即可。将d排个序输出。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } struct tnode { ll x,y; }; vector<tnode>node[110]; int n; int in(int a,int b) { ll x0=node[a][0].x; ll y0=node[a][0].y; ll x1,y1,x2,y2; for(int i=1;i<node[b].size();i++) { x1=node[b][i-1].x;y1=node[b][i-1].y; x2=node[b][i].x;y2=node[b][i].y; if((x1-x0)*(y2-y0)<=(x2-x0)*(y1-y0)) return 0; } x1=node[b][node[b].size()-1].x;y1=node[b][node[b].size()-1].y; x2=node[b][0].x;y2=node[b][0].y; if((x1-x0)*(y2-y0)<=(x2-x0)*(y1-y0)) return 0; return 1; } vector<int>e[110]; queue<int>q; int d[110],ru[110]; priority_queue<int>ans; int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) { for(int k=read();k;k--) { int x=read(); node[i].push_back({x,read()}); } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j)continue; if(in(j,i))// { ru[j]++; e[i].push_back(j); } } } for(int i=1;i<=n;i++) if(ru[i]==0) { d[i]=1; q.push(i); } while(q.size()) { int x=q.front(); q.pop(); for(auto y:e[x]) { ru[y]--; if(ru[y]==0) { d[y]=d[x]+1; q.push(y); } } } for(int i=1;i<=n;i++) if(!e[i].size()) ans.push(-d[i]); while(ans.size()) { printf("%d ",-ans.top()); ans.pop(); } }
I
计数类问题。考虑数字x在第i位的方案对答案的贡献是pow(10,i-1)*(n-1)! *x,计算一下即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } ll n,sum,now,t,mod=1e9+7; int main() { // freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;i++) { sum=sum+read(); t=(t*10+1)%mod; } now=1; for(int i=1;i<n;i++) now=now*i%mod; cout<<t*sum%mod*now%mod; }
J
大胆模拟,注意数组要开ll,否则1e8*32会爆掉。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read() { ll x;scanf("%lld",&x);return x; } ll n,m,a[210][210]; struct node { ll a,b; char c; }o[210][210]; char s[100]; int main() { // freopen("1.in","r",stdin); n=read();m=read(); if(read()&1) { scanf("%s",s); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%lld%c%lldi",&o[i][j].a,&o[i][j].c,&o[i][j].b); printf("%lld%c%lldi ",o[i][j].a,o[i][j].c,o[i][j].b); } printf("\n"); } printf("%ss have\n",s); if(s[4]=='y') for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) o[i][j].a*=8,o[i][j].b*=8; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld%c%lldi ",o[i][j].a,o[i][j].c,o[i][j].b); printf("\n"); } printf("mouths,\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld%c%lldi ",o[i][j].a*2,o[i][j].c,o[i][j].b*2); printf("\n"); } printf("eyes and\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld%c%lldi ",o[i][j].a*4,o[i][j].c,o[i][j].b*4); printf("\n"); } printf("legs."); } else { scanf("%s",s); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%lld",&a[i][j]); printf("%lld ",a[i][j]); } printf("\n"); } printf("%ss have\n",s); if(s[4]=='y') for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]*=8; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld ",a[i][j]); printf("\n"); } printf("mouths,\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld ",a[i][j]*2); printf("\n"); } printf("eyes and\n"); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%lld ",a[i][j]*4); printf("\n"); } printf("legs."); } }
K
注意到ai最多有log(ai)个1,所以 以i位结尾的所有区间的与最多有log(ai)种不同的数,用set维护一下以i位结尾的区间的与。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int read() { int x;scanf("%d",&x);return x; } set<int>ans[200010]; int main() { int n=read(); for(int i=1;i<=n;i++) { int x=read(); ans[i].insert(x); for(auto y:ans[i-1]) ans[i].insert(x&y); printf("%d ",ans[i].size()); } }
L
大胆dp
f[i][j][kk]表示在i行第j列花费了kk活力值能获得的最大的祝福值。
f[i][j][kk]=max(f[i-1][j][kk],f[i][j-1][kk]);
若kk>=a[s[i][j]-'0'],可以更新
f[i][j][kk]=max(f[i][j][kk],max(f[i][j-1][kk-val]+a[val],f[i-1][j][kk-val]+a[val]));
最后输出f[n][m][k]