2021年省A第一场
A、卡片
0到9的卡片各2021张,够从1拼到哪个数字?
3181:用数组存储个数+遍历
int main(){
int have[10],now=1,x;
for (int i=0;i<10;i++) have[i]=2021;
while (1){
x=now;
while (x){//能否拼成
if (have[x%10])have[x%10]-=1;
else break;
x/=10;
}
if (x) break;
else now+=1;
}
cout<<now-1; //最后一个没拼成
return 0;
}
B、直线
40257:斜率为0和斜率不存在的情况共20+21=41条。对正常斜率的点计算k 和 b,因为doubl有精度损失千万不能用k算b和用b算k,都直接根据联立等式算!然后用map去重记录个数即可。
#define maxn 1000
#define maxx 20
#define maxy 21
using namespace std;
struct node{int x,y;};//存储点
map<double,map<double,int> > k; //储存斜率和截距
int main(){
int c=0,cnt=0,t;
node dot[maxn];
for (int i=0;i<maxy;i++){
for (int j=0;j<maxx;j++) dot[c++]={j,i};//记录点
}
for (int i=0;i<c;i++){//较小点
for (int j=i+1;j<c;j++){
if (dot[i].x!=dot[j].x && dot[i].y!=dot[j].y){//有斜率
double x = double(dot[j].y-dot[i].y)/double(dot[j].x-dot[i].x);
double b = double(dot[j].x*dot[i].y-dot[i].x*dot[j].y)/double(dot[j].x-dot[i].x);
if (k[x].find(b)==k[x].end()) cnt+=1;
k[x][b]=1;
}
}
}
cout<<cnt+maxx+maxy;
return 0;
}
C、货物摆放
2430:使得三个因子非降序,求其全排列之和
typedef long long ll;
int main(){
ll in = 2021041820210418,t,cnt=0;
//非降序三个因子
for (ll i=1;i<ll(sqrt(in))+1;i++){
if (in%i==0){
t = in/i;
for (ll j=i;j<ll(sqrt(t))+1;j++){
if (t%j==0){
if (i==j){//两个数字一样
if (i==t/j) cnt+=1;//三个数字一样
else cnt+=3;
}
else if (j==t/j) cnt+=3;//两个数字一样
else cnt+=6;//三个数字不同
}
}
}
}
cout<<cnt;
return 0;
}
D、路径
10266837:动态规划令dp存储长度,dp[1]=0,dp[x]=min(dp[可到达x的i]+i x之间的花费)
int gcd(int x,int y){return y?gcd(y,x%y):x;};
int main(){
int dp[2022],maxi;
dp[1]=0;
for (int i=2;i<2022;i++){
maxi = 99999999;
for (int j=max(1,i-21);j<i;j++){
if (dp[j]+i*j/gcd(i,j)<maxi) maxi=dp[j]+i*j/gcd(i,j); //找最短路径
}
dp[i]=maxi;
}
cout<<dp[2021];
return 0;
}
E、回路计数
881012367360:一开始想的是图+dfs,用cost[i][j]记录路径,2 ^ 21复杂度根本跑不完TAT。所以学习了状压dp,用21位二进制串表示第k栋有没有走过,以后就是哈密顿回路板子题:换成0到20号楼容易理解和计算。
typedef long long ll;
ll cost[25][25],dp[1<<21][25],res=0;
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;};
int main(){
//路径初始化
for (int i=1;i<22;i++){
for (int j=1;j<i;j++){
if (gcd(i,j)==1) cost[i-1][j-1]=cost[j-1][i-1]=1;//互质
else cost[i-1][j-1]=cost[j-1][i-1]=0;
}
cost[i-1][i-1]=0;
}
//状态压缩dp,dp[i][j]表示从i到j的方案数
dp[1][0]=1;
for (int i=1;i<(1<<21);i++){
for (int j=0;j<21;j++){
if (i>>j&1){//当前状态走过j
for (int k=0;k<21;k++){
if (!(i>>k&1) && cost[j][k])//未走过k,j k有路
dp[i+(1<<k)][k]+=dp[i][j];
}
}
}
}
//将所有可回到1的回路求和
for (int i=1;i<22;i++) res+=dp[(1<<21)-1][i];
cout<<res;
return 0;
}
2021年省A第二场
A、双乘积
59375:边遍历边求模
int main(){
int res = 1 , mod=100000;
for (int i=1;i<2022;i+=2) res = (res*i)%mod;
cout<<res;
return 0;
}
B、格点
15698:边遍历边判断
int main(){
int cnt=0;
for (int i=1;i<2022;i++){
for (int j=1;j<2022;j++){
if (i*j<=2021) cnt+=1;
}
}
cout<<cnt;
return 0;
}
C、整数分解
691677274345:乘法分解和加法分解我都形成非递减序列,分情况讨论暴力。但加法分解可套用隔板法模型,共2020个位置放置4个板C(20,4),所以小心long long溢出边乘边除几行代码就搞定 。。。
typedef long long ll;
int main(){
ll cnt=0;
for (int i=1;i<2021;i++){
for (int j=i;j<2021-i;j++){
for (int k=j;k<2021-i-j;k++){
for (int p=k;p<2021-i-j-k;p++){
int q = 2021-i-j-k-p;
if (q>=p){//非递减5个数序列
if (i==j){
if (j==k){
if (k==p){
if (p==q) cnt+=1; //C55 五个数相同
else cnt+=5;//C51四个数相同
}
else{
if (p==q) cnt+=10; //C52 3个数相同,2个数相同
else cnt+=20; //A52 3个数相同,2个数不同
}
}
else{
if (k==p){
if (p==q) cnt+=10; //C52 2个数相同,3个数相同
else cnt+=30;//C51*C42 2个数相同,2个数相同
}
else{//2个数相同
if (p==q) cnt+=30; //C51*C42 2个数相同
else cnt+=60; //A53 3个数不同
}
}
}
else{
if (j==k){
if (k==p){
if (p==q) cnt+=5; //C51 4个数相同
else cnt+=20;//A52 3个数相同,2个数不同
}
else{
if (p==q) cnt+=30;//C51*C42 2个数相同,2个数相同
else cnt+=60; //A53 3个数不同
}
}
else{
if (k==p){
if (p==q) cnt+=20; //A52 3个数相同,2个数不同
else cnt+=60; //A53 3个数不同
}
else{
if (p==q) cnt+=60; //A53 3个数不同
else cnt+=120; //A55 5个数不同
}
}
}
}
else break;//后面更不可能
}
}
}
}
cout<<cnt;
return 0;
}
隔板法,加法分解yyds
typedef long long ll;
int main(){
ll cnt=1;
for (ll i=1;i<5;i++){
cnt = (cnt*(2021-i))/i;//及时除以防止溢出
}
cout<<cnt;
return 0;
}
D、城邦
4046:node结构体存储无向图每条边,按花费递增后,最小生成树の并查集算法将答案找出来。
#include <iostream>
#include <algorithm>
#define maxn 2022
using namespace std;
struct node{int x,y,cost;};
node e[2041220];
int head[maxn],c=0,r=0;
bool cmp(node a,node b){
if (a.cost==b.cost) return (a.x<b.x);
return (a.cost<b.cost);
}
int sum(int x,int y){
int res=0;
int xa=x%10,ya=y%10;
int xb=(x/10)%10,yb=(y/10)%10;
int xc=(x/100)%10,yc=(y/100)%10;
int xd=(x/1000)%10,yd=(y/1000)%10;
if (xa!=ya) res+=xa+ya;
if (xb!=yb) res+=xb+yb;
if (xc!=yc) res+=xc+yc;
if (xd!=yd) res+=xd+yd;
return res;
}
int f(int x){
if (head[x]==x) return x;
return head[x]=f(head[x]);
}
void together(int x,int y){
int a=f(x),b=f(y);
if (a!=b) head[b]=a;
}
int main(){
//图初始化
for (int i=1;i<maxn;i++){
for (int j=i+1;j<maxn;j++) e[c++]={i,j,sum(i,j)};
head[i]=i;
}
sort(e,e+c,cmp);//根据长度排序
for (int i=0;i<c;i++){
if (f(e[i].x)!=f(e[i].y)){//最小生成树
together(e[i].x,e[i].y);
r+=e[i].cost;
}
}
cout<<r;
return 0;
}
E、游戏
1352184317599:用动态规划做的但存不下长度为20210509的dp啊TAT
大佬用递归+循环找因子,跑十几分钟出来了。。。真考试就先放弃E题吧,一般F更简单TAT
typedef long long ll;
ll f[20210510];
ll dfs(int n){
if(f[n] != -1) return f[n];
if(n == 1) return 1;
f[n] = 0;
for (int i=1;i<=n/i;i++)
if(n%i==0){//是因子
f[n]+=dfs(i);
if(n/i!=i&&n/i<n) f[n]+=dfs(n/i);//去掉相同因子和自身的情况
}
return f[n];
}
int main(){
ll ans = 0;
memset(f, -1, sizeof f);
for (int i=1;i<=20210509;i++) ans+=dfs(i);//累加
cout<<ans<<endl;
return 0;
}