A 小念吹气球
思路:
拿个数组标记一下这个题有没有通过的人,如果是一血,就多给个气球。
code:
#include <iostream>
#include <cstdio>
using namespace std;
int n,ans;
bool vis[30];
char ch;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>ch;
if(!vis[ch-'A']){
vis[ch-'A']=true;
ans++;
}
ans++;
}
cout<<ans;
return 0;
}
B You Brought Me A Gentle Breeze on the Field
思路:
感觉像是博弈论,和取火柴这题很像,不过这个不是。因为两人每次操作都是最优的,这意味着双方都不会失误导致局面出现变数,对一个状态显然只有必胜和必败两种状态。
必胜状态后面必定有一个必败状态(也就是面临必胜状态的人一定可以将必败状态留给对手),而必败状态后面都是必胜状态(也就是面临必败状态的人一定会把必胜状态流给对方)。
这样的话,如果取糖果轮次够用,那么拥有额外回合的人一定可以反败为胜,因为必败状态后面肯定有必胜状态。
那么问题就转化成了取糖果能不能一局定胜负,否则拥有额外回合的人一定获胜。有这样几种情况:
- n = 1 n=1 n=1,先手必败
- n ∈ [ 2 , m + 1 ] n\in[2,m+1] n∈[2,m+1],先手必胜
- n > m + 1 n>m+1 n>m+1,一轮不能定胜负,有额外回合的人一定获胜
如果没有额外回合规则,那么这就和取火柴的题是一样的了,或者说是一种 巴什博弈,参考2
code:
#include <iostream>
#include <cstdio>
using namespace std;
int T,n,m,p;
int main(){
cin>>T;
while(T--){
cin>>n>>m>>p;
if(n==1)puts("YangQiShaoNian");
else if(n<=m+1)puts("XiaoNian");
else puts((p)?"YangQiShaoNian":"XiaoNian");
}
return 0;
}
C 氧气少年的水滴 2
思路:
由于一个水珠爆裂后只有可能对左右两边产生影响,被影响的水珠才有可能爆裂。所以我们直接看爆裂的水珠两边的水珠就可以了,用两个指针模拟水珠爆裂的过程。
code:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=2e5+5;
int T,a[maxn],n,p;
int main(){
cin>>T;
while(T--){
cin>>n>>p;
for(int i=1;i<=n;i++)
cin>>a[i];
if(a[p]+1<10){
puts("0 0");
continue;
}
int l=p-1,r=p+1,lc=1,rc=1;
bool f;
while(true){
f=false;
while(l>=1 && a[l]+lc>=10){
lc-=10-a[l];
l--;
lc++;
rc++;
f=true;
}
while(r<=n && a[r]+rc>=10){
rc-=10-a[r];
r++;
lc++;
rc++;
f=true;
}
if(!f)break;
}
printf("%d %d\n",(l<1)?lc:0,(r>n)?rc:0);
}
return 0;
}
D 氧气少年的 LCM
思路:
构造题,不难想到 x y xy xy,要凑的 l c m ( x , y ) lcm(x,y) lcm(x,y) 以及使用两种操作造出来的所有数都一定含有因数 g c d ( x , y ) gcd(x,y) gcd(x,y),因此不妨一开始先不看 g c d ( x , y ) gcd(x,y) gcd(x,y),把 x y xy xy 中的 g c d ( x , y ) gcd(x,y) gcd(x,y) 除掉,这样 x y xy xy 一定是互质的, l c m = x ∗ y , g c d = 1 lcm=x*y,gcd=1 lcm=x∗y,gcd=1,不妨再给 x y xy xy 排下序,使得 x < y x<y x<y。
如果我们取一次操作1,就能得到一个1,再取一次就能得到两个,然后对两个1进行操作2,就能得到一个2,再进行一次得到两个2,类推,能得到两个4,两个8…于是我们就可以使用二进制来凑出答案了。
答案要求操作不超过200次,而二进制最多跑到 2 60 2^{60} 260 不到,也就是最多使用120次准备操作,而凑数再用60次,总的操作次数一定不会超过200。
code:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=205;
int T;
ll x,y;
ll gcd(ll a,ll b){
while(b)b^=a^=b^=a%=b;
return a;
}
struct opt{
int op;
ll n1,n2;
}opt[maxn*maxn];
int num;
ll lowbit(ll x){return x&-x;}
int main(){
cin>>T;
while(T--){
cin>>x>>y;
if(x>y)swap(x,y);
ll gd=gcd(x,y),lm=x*y/gd/gd;
x/=gd;
y/=gd;
if(lm==y){
puts("0");
continue;
}
num=0;
opt[++num].op=1;
opt[num].n1=x;
opt[num].n2=y;
opt[++num].op=1;
opt[num].n1=x;
opt[num].n2=y;
for(int i=1;i<=59 && (1ll<<i)<=lm;i++){
opt[++num].op=2;
opt[num].n1=1ll<<(i-1);
opt[num].n2=1ll<<(i-1);
opt[++num].op=2;
opt[num].n1=1ll<<(i-1);
opt[num].n2=1ll<<(i-1);
}
ll lst=lowbit(lm);
lm-=lowbit(lm);
while(lm){
opt[++num].op=2;
opt[num].n1=lst;
opt[num].n2=lowbit(lm);
lst+=lowbit(lm);
lm-=lowbit(lm);
// cout<<lm<<endl;
}
printf("%d\n",num);
for(int i=1;i<=num;i++)
printf("%d %lld %lld\n",opt[i].op,opt[i].n1*gd,opt[i].n2*gd);
}
return 0;
}
E 氧气少年逛超市 3
思路:
这题出题人题解给的讲解很详细严谨。这题是个贪心+dp。把出题人题解的图粘过来一下:
不管使用哪个券,一定是用在大价钱的东西上最好,所以前 m i n ( a + b , n ) min(a+b,n) min(a+b,n) 个商品一定用券。对折扣券,如果一个商品要用券,一定是先使用小的最好,这样折扣的钱会更多。对立减券,一定是先用大的最好,这样减掉的钱最多。因此规定了这几个规则后,就可以使用dp进行递推了。
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=5005;
const int inf=1e9;
int T,n,p[maxn];
int a,x[maxn];
int b,y[maxn];
double dp[maxn][maxn];//前i个物品 用j个折扣券 i-j个立减券
int main(){
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++)cin>>p[i];
cin>>a;
for(int i=1;i<=a;i++)cin>>x[i];
cin>>b;
for(int i=1;i<=b;i++)cin>>y[i];
sort(p+1,p+n+1,greater<int>());
sort(x+1,x+a+1);
sort(y+1,y+b+1,greater<int>());
for(int i=1;i<=min(a+b,n);i++){
for(int j=0;j<=min(i,a);j++){
dp[i][j]=inf;
//尝试使用折扣券
if(j>=1 && j<=a)dp[i][j]=min(dp[i][j],dp[i-1][j-1]+x[j]*p[i]/100.0);
//尝试使用立减券
if(i-j>=1 && i-j<=b)dp[i][j]=min(dp[i][j],dp[i-1][j]+max(0,p[i]-y[i-j]));
// printf("dp(%d,%d)=%lf\n",i,j,dp[i][j]);
}
}
double ans=inf;
for(int i=0;i<=a;i++)
ans=min(ans,dp[min(a+b,n)][i]);
for(int i=a+b+1;i<=n;i++)
ans+=p[i];
printf("%lf\n",ans);
}
return 0;
}