2019.10.12
自闭了
T1:
秦师兄除了热衷于游戏开发,平时就爱好打打牌,中学时代外出比赛时就喜欢跟女生打牌, 并且经常不留痕迹地打赢牌打输,以至于机房小伙伴们都亲切地称他为“情愿输”,最近师兄 迷上了风靡全球的德州扑克。
德州扑克是一款风靡全球的扑克游戏。德州扑克一共有 52 张牌,没有王牌。每个玩家分 两张牌作为“底牌”,五张由荷官陆续朝上发出的作为公共牌。 摊牌阶段比大小的规则是,用自己的 2 张底牌和 5 张公共牌结合在一起,选出 5 张牌,不 论手中的牌使用几张(甚至可以不用手中的底牌),凑成最大的成牌,跟其他玩家比大小。比 牌先比牌型,大的牌型大于小的牌型
在这里我们简化了德克萨斯扑克的规则,消除花色对牌型的影响。
这里我们把牌型一般分 为 7 种,从大到小为:
1.四条:有四张同一点数的牌。 如:4 4 4 4 9
2.葫芦:三张同一点数的牌,加一对其他点数的牌。 如:3 3 3 10 10
3.顺子:五张顺连的牌。 如:5 4 3 2 A (此牌型为最小的顺子)
4.三条:仅有三张同一点数的牌,其余两张点数不同。 如:9 9 9 5 3
5 两对:两张相同点数的牌,加另外两张相同点数的牌。 如:K K 5 5 2
6.一对:仅有两张相同点数的牌。 如:10 10 9 4 2
7.高牌:不符合上面任何一种牌型的牌型,由单牌且不连续不同花的组成,以点数决定大 小。 如:A 10 9 5 3
若两个牌牌型相同,则比较点数,点数比较为从两副牌最大的牌到最小的牌依次开始比较 大小,若出现某张牌大于对手的牌,则获胜。如 A Q 8 8 3 和 A K 8 3 3,先比较最大的,两幅 都是 A,继续比较次大的,K>Q,则第二副牌获胜。
(注:通常情况下,点数比大小的方案为 A>K>Q>J>10>9>8>7>6>5>4>3>2,除了顺 子 5 4 3 2 A 中 A 作为比 2 还要小的值,顺子只能是 A,K,Q,J,10,9,8,7,6,5,4,3,2,A 该顺序中任意相 邻两张牌)
现在秦师兄拿到了自己的 2 张底牌和 5 张公共牌,他想问你可以凑成的最大的成牌是哪 5 张,并按照从大到小的顺序输出
SB模拟!!!,调了1.5h,这场直接炸了
思路:暴力枚举所有状态比较
code(没什么用,这种题就是练代码能力的):
#include<bits/stdc++.h>
using namespace std;
int a[10],ans[20],tmp[20],c1[20],c2[20];
char s[5];
int check=8,now;
bool flag5=1;
bool vis[10];
vector<int>num1[10],num2[10];
inline int change(char c)
{
if(c=='A')return 14;
if(c=='K')return 13;
if(c=='Q')return 12;
if(c=='J')return 11;
return 233333;
}
inline char rechange(int x)
{
if(x==14||x==1)return 'A';
if(x==13)return 'K';
if(x==12)return 'Q';
if(x==11)return 'J';
return 'c';
}
inline bool cmp(int x,int y){return x>y;}
inline void compare()
{
int tot=0;
for(int i=1;i<=7;i++)if(vis[i])tmp[++tot]=a[i];
sort(tmp+1,tmp+5+1,cmp);sort(ans+1,ans+5+1,cmp);
memset(c1,0,sizeof(c1));
for(int i=1;i<=5;i++)num1[i].clear(),num2[i].clear();
for(int i=1;i<=5;i++)
{
c1[tmp[i]]++;
if(tmp[i]==14)c1[1]++;
}
for(int i=2;i<=14;i++)if(c1[i])num1[c1[i]].push_back(i);
memset(c2,0,sizeof(c2));
for(int i=1;i<=5;i++)
{
c2[ans[i]]++;
if(ans[i]==14)c2[1]++;
}
for(int i=2;i<=14;i++)if(c2[i])num2[c2[i]].push_back(i);
bool flag=0,flag1=0,flag2=0;
int k1=7,k2=7;
if(num1[4].size())k1=1,flag1=1;
else if(num1[3].size()&&num1[2].size())k1=2,flag1=1;
if(!flag1)
{
bool flag3=1;
int cnt=0;
for(int i=1;i<=14;i++)if(c1[i]>1)flag3=0;
if(flag3)
{
for(int i=14;i;i--)
if(c1[i])
{
cnt++;
if(cnt==5){k1=3,flag1=1;break;}
}
else cnt=0;
}
}
if(!flag1)
{
if(num1[3].size())k1=4,flag1=1;
else if(num1[2].size()>=2)k1=5,flag1=1;
else if(num1[2].size())k1=6,flag1=1;
}
if(!flag1)k1=7,flag1=1;
if(num2[4].size())k2=1,flag2=1;
else if(num2[3].size()&&num2[2].size())k2=2,flag2=1;
if(!flag2)
{
bool flag3=1;
int cnt=0;
for(int i=1;i<=14;i++)if(c2[i]>1)flag3=0;
if(flag3)
{
for(int i=14;i;i--)
if(c2[i])
{
cnt++;
if(cnt==5){k2=3,flag2=1;break;}
}
else cnt=0;
}
}
if(!flag2)
{
if(num2[3].size())k2=4,flag2=1;
if(num2[2].size()>=2)k2=5,flag2=1;
else if(num2[2].size())k2=6,flag2=1;
}
if(!flag2)k2=7,flag2=1;
bool flag4=1;
if(k1<k2)flag=1;
else if(k1==k2)
{
sort(tmp+1,tmp+5+1);sort(ans+1,ans+5+1);
if(k1==3)
{
if((c1[13]&&c1[14])||(c2[13]&&c2[14]))flag4=0;
for(int i=1;i<=5;i++)if(tmp[i]==14&&flag4)tmp[i]=1;
for(int i=1;i<=5;i++)if(ans[i]==14&&flag4)ans[i]=1;
sort(tmp+1,tmp+5+1,cmp);sort(ans+1,ans+5+1,cmp);
for(int i=1;i<=5;i++)if(tmp[i]>ans[i]){flag=1;break;}
}
else for(int i=1;i<=5;i++)if(tmp[i]>ans[i]){flag=1;break;}
}
if(flag)
{
check=k1;flag5=flag4;
for(int i=1;i<=5;i++)ans[i]=(tmp[i]==1)?14:tmp[i];
}
for(int i=1;i<=5;i++)ans[i]=(ans[i]==1)?14:ans[i];
}
void dfs(int dep)
{
if(dep==6){compare();return;}
for(int i=1;i<=7;i++)
{
if(vis[i])continue;
vis[i]=1;dfs(dep+1);vis[i]=0;
}
}
int main()
{
for(int i=1;i<=7;i++)
{
scanf("%s",s+1);
if(s[1]>='A'&&s[1]<='Z')a[i]=change(s[1]);
else
{
int now=1,res=0;
while(s[now]>='0'&&s[now]<='9')res=res*10+s[now]-'0',now++;
a[i]=res;
}
}
dfs(1);
//printf("check::%d\n",check);
//vis[4]=vis[2]=vis[3]=vis[5]=vis[6]=1;compare();
if(check==3)for(int i=1;i<=5;i++)if(ans[i]==14&&flag5)ans[i]=1;
sort(ans+1,ans+5+1,cmp);
for(int i=1;i<=5;i++)
if(ans[i]<=10&&ans[i]!=1)cout<<ans[i]<<' ';
else cout<<rechange(ans[i])<<' ';
return 0;
}
T2:
秦师兄作为一个游戏专家,他最近迷上了一款帝国战争为主题的即时策略游戏。为了扩大 自己的疆域,秦师兄需要在自己建设的帝国内组建一支部队投入战斗。
秦师兄手下有
秦师兄想知道最小的混乱度是多少。
右端点
用两个前缀和维护
考虑枚举右端点,贪心选取接近
考虑序列是单调不减的,因此平均值是单调的,所以队长的选取有决策单调性,双指针维护即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+10;
int n,K;
int a[maxn],sum1[maxn];
int ans=4253453345425343342;
int sum2[maxn];
inline int calc(int l,int r,int x){return sum2[r]-sum2[l-1]+K*a[x]*a[x]-2*a[x]*(sum1[r]-sum1[l-1]);}
signed main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%lld%lld",&n,&K);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)sum1[i]=sum1[i-1]+a[i],sum2[i]=sum2[i-1]+a[i]*a[i];
for(int i=K,j=1;i<=n;i++)
{
while(j<i-K+1)j++;
while(calc(i-K+1,i,j)>=calc(i-K+1,i,j+1)&&j<i)j++;
ans=min(ans,calc(i-K+1,i,j));
}
printf("%lld",ans);
return 0;
}
T3:
别看秦师兄平时一副热爱打游戏的样子,但在信息学计数题上的造诣可谓是颇深,你看在这 次模拟赛中,他就除了这样一道计数题。
秦师兄觉得图论很有趣。因此有一个
秦师兄还想融合一些数论元素。因此 i 向 j 连边当且仅当 i 整除 j。请注意一个正整数必定 整除它自身,因而秦师兄允许了自环的存在。 秦师兄觉得还不够好,因为所有的正整数都整除 0。
于是她新建了一个编号为 0 的点,并 让 1−
秦师兄现在很满意。她写下了下面 4 个问题交给你来解决。
-
求出从 1 到
q1 ,且长度恰为 2 的路径个数。 -
求出从 1 到 0,且长度不大于
q2 的路径个数。 -
求出从 1 到 0,没有经过重复的点,且长度不大于
q3 的路径个数。 -
令一条路径的权值为它经过的所有的点的编号之和。求出 (问题 3.)中所有路径的权值 和。
所有问题的答案对
每个测试点的问题 1、4 各占 20% 的分数,2、3 各占 30%的分数。 请保证你的输出文件完整(包含 4 个整数),否则不能得分。
依次解决每一问:
第一问:路径长度为2,中途只会经过一个点,这个点显然是
第二问:
设
第三问:
设
发现不走自环最多走
结合以上算法可以得到
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1e9+7;
int n,q1,q2,q3,ans1,ans2,ans3,ans4;
int f[10100][10100],g[maxn][30],h[maxn][30];
signed main()
{
scanf("%lld%lld%lld%lld",&n,&q1,&q2,&q3);
//problem 1
for(int i=1;i*i<=q1;i++)if(q1%i==0){ans1++;if(q1!=i*i)ans1++;}
if(!q1)ans1=n;
if(n>100000){printf("%lld 0 0 0",ans1);return 0;}
//problem 2
if(n<10000&&q2<=10000)
{
for(int i=1;i<=n;i++)f[i][1]=1;
for(int j=2;j<=q2;j++)
for(int i=1;i<=n;i++)
for(int k=1;k*i<=n;k++)
f[i][j]=(f[i][j]+f[k*i][j-1])%mod;
for(int i=0;i<=q2;i++)ans2=(ans2+f[1][i])%mod;
}
//problem 3/4
int t=(int)log(n)+5;
for(int i=1;i<=n;i++)g[i][1]=1,h[i][1]=i;
for(int j=2;j<=min(q3,t+1);j++)
for(int i=1;i<=n;i++)
for(int k=2;k*i<=n;k++)
g[i][j]=(g[i][j]+g[k*i][j-1])%mod,h[i][j]=((h[i][j]+h[k*i][j-1])%mod+g[k*i][j-1]*i%mod)%mod;
for(int i=0;i<=min(t+1,q3);i++)ans3=(ans3+g[1][i])%mod,ans4=(ans4+h[1][i])%mod;
printf("%lld %lld %lld %lld",ans1,ans2,ans3,ans4);
return 0;
}
考虑用第三问的答案推出第二问的答案
设有一条从
根据组合恒等式:
于是
以上做法能拿到
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
const int mod=1e9+7;
int n,q1,q2,q3,ans1,ans2,ans3,ans4;
int C[35],inv[35];
int f[maxn][30],g[maxn][30];
signed main()
{
scanf("%lld%lld%lld%lld",&n,&q1,&q2,&q3);
//problem 1
for(int i=1;i*i<=q1;i++)if(q1%i==0){ans1++;if(q1!=i*i)ans1++;}
if(!q1)ans1=n;
if(n>100000){printf("%lld 0 0 0",ans1);return 0;}
//problem 3/4
int t=(int)log(n)+10;
for(int i=1;i<=n;i++)f[i][1]=1,g[i][1]=i;
for(int j=2;j<=t;j++)
for(int i=1;i<=n;i++)
for(int k=2;k*i<=n;k++)
f[i][j]=(f[i][j]+f[k*i][j-1])%mod,g[i][j]=((g[i][j]+g[k*i][j-1])%mod+f[k*i][j-1]*i%mod)%mod;
for(int i=0;i<=min(t,q3);i++)ans3=(ans3+f[1][i])%mod,ans4=(ans4+g[1][i])%mod;
//problem 2
C[0]=inv[1]=1;
for(int i=2;i<=t;i++)inv[i]=(mod-mod/i)%mod*inv[mod%i]%mod;
for(int i=1;i<=t;i++)C[i]=C[i-1]*inv[i]%mod*(q2-i+1+mod)%mod;
//for(int i=1;i<=t;i++)printf("test::%lld %lld\n",C[i],inv[i]);
for(int i=1;i<=min(q2,t);i++)ans2=(ans2+f[1][i]*C[i]%mod)%mod;
printf("%lld %lld %lld %lld",ans1,ans2,ans3,ans4);
return 0;
}
考虑正解:
考虑DP到i,那么i能到达的节点有