good problems-9
1. D2. Xor-Subsequence (hard version)
题解
easy版本
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int dp[maxn],a[maxn];
bool check(int p,int p1){
if((a[p]^p1)<(a[p1]^p))return 1;
return 0;
}
void solve(){
int n=read();
for(int i=0;i<n;i++){
a[i]=read();dp[i]=0;
}
int ans=0,maxx=0;
for(int i=0;i<n;i++){
int pos=(i>>8)<<8;
dp[i]=1;
for(int j=pos;j<i;j++){
if(check(j,i))dp[i]=max(dp[i],dp[j]+1);
}
ans=max(ans,dp[i]);
}
cout<<ans<<endl;
return ;
}
int main(){
int t=read();
while(t--)solve();
return 0;
}
hard
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=6e6+101;
const int MOD=998244353;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int cnt,tr[maxn][2],f[maxn][2],dp[maxn],a[maxn],n;
void insert(int x,int id){
int u=0;
for(int i=30;i>=0;i--){
int bit=(x>>i)&1;
if(!tr[u][bit])tr[u][bit]=++cnt;
u=tr[u][bit];
f[u][(id>>i)&1]=max(f[u][(id>>i)&1],dp[id]);
}
return ;
}
int query(int x,int k){
int u=0,ans=1;
for(int i=30;i>=0;i--){
int bit=(x>>i)&1,rev=tr[u][bit^1];
ans=max(ans,f[rev][((k>>i)&1)^1]+1);
u=tr[u][bit];
if(!u)break;
}
return ans;
}
void solve(){
n=read();for(int i=0;i<n;i++)a[i]=read(),dp[i]=0;
for(int i=0;i<=cnt;i++)tr[i][0]=tr[i][1]=f[i][0]=f[i][1]=0;
cnt=0;int ans=0;
for(int i=0;i<n;i++){
dp[i]=query(a[i]^i,a[i]);
insert(a[i]^i,i);
ans=max(ans,dp[i]);
}
printf("%d\n",ans);
return ;
}
int main(){
int t=read();
while(t--)solve();
return 0;
}
2.G - Yet Another RGB Sequence
题意是求字符串个数,字符串包含r个R,g个G,b个B,和k个RG
首先考虑安排k个RG,b个B,r-k个R,方案数=
那么考虑g-k个G能填哪
不难发现有spots=(b+k+1)个空能填G,(B和RG的后面都可以填G,+1是因为开头也能填)
那么每个空可以填多个G,经典排列组合问题
那么方案数=
所以答案就是
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=5e6+101;
const int MOD=998244353;
const ll inf=2147383647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
ll power(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%MOD;
y>>=1;x=x*x%MOD;
}
return ans;
}
ll r,g,b,k;
vector<ll>fac(maxn),inv(maxn);
ll C(ll n,ll m){
if(n==m || m==0)return 1;
ll ans=fac[n]*inv[m]%MOD;
return ans*inv[n-m]%MOD;
}
int main(){
r=read();g=read();b=read();k=read();
fac[0]=1;for(ll i=1;i<=r+g+b;i++)fac[i]=fac[i-1]*i%MOD;
inv[n]=power(fac[n],MOD-2);for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%MOD;inv[0]=1;
ll ans=1;
ans=ans*fac[k+r-k+b]%MOD;ans=ans*inv[k]%MOD*inv[r-k]%MOD*inv[b]%MOD;
ans=ans*C(g-k+b+k,g-k)%MOD;
cout<<(ans%MOD+MOD)%MOD;
return 0;
}
3.G. Two Merged Sequences
题意:是否能将序列分成严格递增和严格递减两个序列
题解:
我们可以发现,假如一个数被分进了上升序列,那它就是上升序列里面最大的数;如果分进下降序列,
它就是最小的数。所以,根据贪心的思路,假如我们把一个数分进了上升序列,我们只需要考虑在这种
情况下,下降序列最后一个数最大可以是多少。据此,考虑dp。
dp[i][0]表示前i个数,i是上升序列最后一个,前i-1个下降序列的最大值
dp[i][1]表示前i个数,i是下降序列最后一个,前i-1个上升序列的最小值
pre[i][0]表示第i个数是上升序列最后一个,第i-1个数是0/1(0表示是上升序列的,1是下降序列的),来记录方案
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e6+101;
const int MOD=998244353;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int n,a[maxn],dp[maxn][2],pre[maxn][2];
//dp[i][0]表示前i个数,i是上升序列最后一个,前i-1个下降序列的最大值
//dp[i][1]表示前i个数,i是下降序列最后一个,前i-1个上升序列的最小值
int main(){
n=read();for(int i=1;i<=n;i++)a[i]=read(),dp[i][0]=-inf,dp[i][1]=inf;
dp[1][0]=inf;dp[1][1]=-inf;
for(int i=2;i<=n;i++){
if(a[i]>a[i-1])dp[i][0]=dp[i-1][0],pre[i][0]=0;
if(a[i]>dp[i-1][1] && a[i-1]>dp[i][0]){
dp[i][0]=a[i-1];
pre[i][0]=1;
}
if(a[i]<a[i-1])dp[i][1]=dp[i-1][1],pre[i][1]=1;
if(a[i]<dp[i-1][0] && a[i-1]<dp[i][1]){
dp[i][1]=a[i-1];
pre[i][1]=0;
}
}
if(dp[n][0]==-inf && dp[n][1]==inf)puts("NO");
else {
puts("Yes");
int pos=0;
if(dp[n][1]!=inf)pos=1;
vector<int>ans(n+1);
for(int i=n;i>=1;i--){
if(pos==0)ans[i]=0;
else ans[i]=1;
pos=pre[i][pos];
}
for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
}
return 0;
}
4.B - Plus and AND
题意:每次操作,可以使一个变成,问最多M次操作,求k个数的AND和最大
题解:
不难发现是贪心题
对于这种异或贪心题都可以按照以下来写
每次判断ans+(1<<d)是否成立
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=998244353;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
vector<ll>a(maxn);
int n,m,k;
bool check(int x){
vector<ll>b(n+1);
for(int i=1;i<=n;i++){
int j=30;
while(j>=0 && (( (a[i]>>j) | ((~x)>>j) )&1) )j--; //从高位到低位找到第一个位置j,x在j位置有1,a[i]在j位置没1
if(j>=0){
b[i]=(x&((1<<(j+1))-1))-(a[i]&((1<<j)-1));
}
}
sort(b.begin()+1,b.begin()+n+1);
ll ans=0;
for(int i=1;i<=k;i++)ans+=b[i];
return ans<=m;
}
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)a[i]=read();
ll ans=0;
for(int d=30;d>=0;d--){
if(check(ans|(1<<d)))ans|=1<<d;
}
cout<<ans;
return 0;
}
5.C. Square Subsets
题意:给一序列,问有多少种方案,使得选中的数乘起来是个完全平方数
题解:
考虑完全平方数的性质: 完全平方数x质因数分解,每个质因数的次方都是偶数
再考虑题目限制
那么70以内的质数只有19个
考虑把19个质因数转化成二进制表示形式,第i位=0/1表示第i个质数的次方是偶数还是奇数
所以我们可以写出一个暴力dp,设表示序列中前i个数,选中数的乘积质因数分解后的状态位j的方案数
但时间复杂度超时
考虑如何优化?
因为,并且相同的数只有选偶数次和奇数次两种区别
那么我们不从枚举,我们从枚举
设
那么
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
ll power(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%MOD;
y>>=1;x=x*x%MOD;
}
return ans;
}
ll poss[71][2],dp[2][1<<20];
int n,bk[71],mask[71];
int pri[20]={2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67};
int main(){
n=read();
for(int i=1;i<=n;i++)bk[read()]++;
for(int i=1;i<71;i++){
if(bk[i]==0){
poss[i][0]=1;poss[i][1]=0;
continue;
}
poss[i][0]=poss[i][1]=power(2,bk[i]-1);
}
for(int i=1;i<71;i++){
for(int j=0;j<19;j++){
int cnt=0,now=i;
while(now%pri[j]==0)now/=pri[j],cnt++;
if(cnt&1)mask[i]|=1<<j;
}
}
dp[0][0]=1;
for(int i=1;i<71;i++){
int ii=(i-1)%2;
for(int j=0;j<(1<<19);j++){
dp[i&1][j]=dp[ii][j]*poss[i][0]%MOD;
dp[i&1][j]+=dp[ii][j^mask[i]]*poss[i][1]%MOD;
dp[i&1][j]%=MOD;
}
}
cout<<(dp[70&1][0]-1+MOD)%MOD; //减1 都不选的情况
return 0;
}
6.E. Madoka and The Best University
题意:求
题解:
在数论中求一个公式的解我们一定要选择好枚举方式,这样才能减少时间复杂度。
我们先考虑枚举顺序,如果我们先枚举a,再枚举b,这样可以确定c但是显然是n^2的,我们直接pass。枚举c显然显然没有任何道理可言,因此我们只能考虑枚举gcd(a, b)。
如果我们枚举的话,下一层循环就要枚举a + b,a + b的可能值是2i, 3i, 4i...这样的话时间复杂度就降到了。但是如何计算答案呢?我们如何将a + b一种情况计算出所有的(a,b)可能情况的值呢?
首先此时,(a,b)是不能确定的,但是c可以确定的。我们可以列一个方程,设,此时代入方程就是 那么x/i的取值的可能就是与(j / i)互质且小于(j / i)的数,所以x的取值就是phi[j / i]。
点击查看代码
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include <bits/stdc++.h>
#define ll long long
#define pa pair<ll,int>
using namespace std;
const int maxn=4e5+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int gcd(int a,int b){
if(b>a)swap(a,b);
if(b==0)return a;
return gcd(b,a%b);
}
ll lcm(ll a,ll b){
return a*b/gcd(a,b);
}
int prime[maxn],tot,is[maxn],phi[maxn];
void get_phi(ll n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!is[i])prime[++tot]=i,phi[i]=i-1;
for(int j=1;j<=tot && prime[j]*i<=n;j++){
is[i*prime[j]]=1;
if(i%prime[j]==0){//说明已经有了一个prime[i]
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
return ;
}
int main(){
int n=read();get_phi(n);
ll ans=0;
for(int i=1;i<=n;i++)for(int j=i*2;j<n;j+=i){
ans+=lcm(i,n-j)*(ll)phi[j/i]%MOD;
ans%=MOD;
}
printf("%lld\n",(ans+MOD)%MOD);
return 0;
}
7.E - Erasing Vertices 2
题意、:给定一个无向图带有n个点m条边,每个点都有权值。每次要删除一条边,一次删除的代价是从这个点直接相邻的未被删除的点的权值和,使n次删除操作后的最大值最小,求其最小值。
题解:
总代价满足单调性,考虑二分
对于当前二分的值x
若某些点的相邻权值小于x,便可以删除,同时与它相邻的点的价值减小,若小于x那么也可以删除,用队列维护
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=2e5+101;
const int MOD=1e9+7;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int n,m,w[maxn];
vector<vector<int> >G(maxn);
ll a[maxn],b[maxn];
bool check(ll x){
unordered_map<int,int>mm;queue<int>q;
for(int i=1;i<=n;i++){
if(a[i]<=x)q.push(i),mm[i]=1;
b[i]=a[i];
}
while(!q.empty()){
int u=q.front();q.pop();
for(auto v:G[u]){
if(!mm[v]){
b[v]-=w[u];
if(b[v]<=x && !mm[v])q.push(v),mm[v]=1;
}
}
}
int cnt=0;
for(int i=1;i<=n;i++)cnt+=mm[i];
return n==cnt;
}
int main(){
n=read();m=read();
ll l=0,r=0;
for(int i=1;i<=n;i++){
w[i]=read();
r+=(ll)w[i];
}
for(int i=1;i<=m;i++){
int x=read(),y=read();
G[x].pb(y);G[y].pb(x);
a[x]+=(ll)w[y];
a[y]+=(ll)w[x];
}
ll ans=0;
while(r>=l){
ll mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans;
return 0;
}
8.F - Exactly K Steps
题意:给一个n个点的树,q此询问,每次询问问与x点距离为k的点(任意输出一个),没有则输出-1
题解:
首先考虑x最大能延伸到哪?显然是树的直径的一个端点
那么我们就预处理出树的直径d1,d2
那么找距离为k的点就分别从x向d1和d2倍增找距离为k的点
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=4e5+101;
const int MOD=19980829;
const ll inf=2147483647;
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int n;
struct wzq{
int x,k,ans;
}q[maxn];
int tot,head[maxn],nx[maxn],to[maxn];
void add(int x,int y){to[++tot]=y;nx[tot]=head[x];head[x]=tot;}
int dep[maxn],f[maxn][21],d;
void dfs(int x,int fa){
dep[x]=dep[fa]+1;
if(dep[x]>dep[d])d=x;
for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=nx[i]){
int v=to[i];if(v==fa)continue;
f[v][0]=x;dfs(v,x);
}
return ;
}
int get_ans(int x,int k){
for(int i=20;i>=0;i--){
if((k&(1<<i)))x=f[x][i];
}
return x?x:-1;
}
int main(){
n=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);add(y,x);
}
int Q=read();
for(int i=1;i<=Q;i++)q[i].x=read(),q[i].k=read();
dfs(1,0);
for(int i=1;i<=n;i++){
dep[i]=0;
for(int j=0;j<=20;j++)f[i][j]=0;
}
dfs(d,0);
for(int i=1;i<=Q;i++)q[i].ans=get_ans(q[i].x,q[i].k);
for(int i=1;i<=n;i++){
dep[i]=0;
for(int j=0;j<=20;j++)f[i][j]=0;
}
dfs(d,0);
for(int i=1;i<=Q;i++)q[i].ans=max(q[i].ans,get_ans(q[i].x,q[i].k)),cout<<q[i].ans<<endl;
return 0;
}
9.Ex - Odd Sum
题意:给定一个长度为n的整数序列A,问有多少种不同方案,使得选中的数加起来为m
题解:
因为很小,所以我们每次枚举值来进行ntt
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=5e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-12;
int read(){
int x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
ll power(ll x,ll y){
ll ans=1;
while(y){
if(y&1)ans=ans*x%MOD;
y>>=1;x=x*x%MOD;
}
return ans;
}
ll fac[maxn],inv[maxn];
void init(int n){
fac[0]=1;for(ll i=1;i<=n;i++)fac[i]=fac[i-1]*i%MOD;
inv[n]=power(fac[n],MOD-2);for(ll i=n-1;i;i--)inv[i]=inv[i+1]*(i+1)%MOD;
return ;
}
ll C(int m,int n){
if(m==0 || n==m)return 1;
ll ans=fac[n]*inv[m]%MOD;
return ans*inv[n-m]%MOD;
}
typedef vector<ll> Poly;
int rev[maxn];
void get(int bit){
for(int i=0;i<(1<<bit);i++)rev[i]=(rev[i>>1]>>1)|((1&i)<<(bit-1));
return ;
}
void ntt(ll *a,int n,int f){
get(log2(n));
for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1){
ll wn=power(3,(MOD-1)/(i<<1))%MOD;
if(f==-1)wn=power(wn,MOD-2);
for(int j=0;j<n;j+=i<<1){
ll w=1,x,y;
for(int k=0;k<i;k++,w=wn*w%MOD){
x=a[k+j];y=a[k+j+i]*w%MOD;
a[j+k]=(x+y)%MOD;a[j+k+i]=(x-y)%MOD;
}
}
}
if(f==1)return ;
int nv=power(n,MOD-2);
for(int i=0;i<n;i++)a[i]=a[i]*nv%MOD;
return ;
}
ll F1[maxn],F2[maxn];
Poly mul(Poly A,Poly B){ //求多项式A*B
int n=A.size(),m=B.size(),lens=n+m-1;
int bit=ceil(log2(lens));lens=(1<<bit);
for(int i=0;i<lens;i++)F1[i]=F2[i]=0;
for(int i=0;i<n;i++)F1[i]=A[i];
for(int i=0;i<m;i++)F2[i]=B[i];
ntt(F1,lens,1);ntt(F2,lens,1);
for(int i=0;i<lens;i++)F1[i]=F1[i]*F2[i]%MOD;
ntt(F1,lens,-1);
Poly ans;
for(int i=0;i<n+m-1;i++)ans.push_back(F1[i]);
return ans;
}
int n,m,cnt[11];
int main(){
n=read();m=read();init(1e6);
for(int i=1;i<=n;i++)cnt[read()]++;
Poly f_odd(1),f_even(1);
//f_odd 选奇数个,f_even选偶数个
f_even[0]=1;
int limi=1;
for(int i=1;i<=10;i++){
Poly g_odd(cnt[i]*i+1),g_even(cnt[i]*i+1);
for(int j=1;j<=cnt[i];j+=2)g_odd[j*i]=C(j,cnt[i]);
for(int j=0;j<=cnt[i];j+=2)g_even[j*i]=C(j,cnt[i]);
Poly fff_odd,ff_odd;
ff_odd=mul(f_odd,g_even);
fff_odd=mul(f_even,g_odd);
Poly fff_even,ff_even;
ff_even=mul(f_even,g_even);
fff_even=mul(f_odd,g_odd);
limi+=cnt[i]*i;
Poly now_f_odd(limi+1),now_f_even(limi+1);
for(int j=0;j<=limi;j++){
now_f_odd[j]=(ff_odd[j]+fff_odd[j])%MOD;
now_f_even[j]=(ff_even[j]+fff_even[j])%MOD;
}
f_odd=now_f_odd;
f_even=now_f_even;
}
if(m<f_odd.size())printf("%lld\n",(f_odd[m]%MOD+MOD)%MOD);
else puts("0");
return 0;
}
10.D. Letter Picking
题意:Alice 和 Bob 玩游戏,游戏规则是这样的:给定一个长度为偶数的小写字母字符串 s ,Alice 和 Bob 也有自己的字符串,最初是空的。 爱丽丝先手,然后他们交替操作。在一个操作中,玩家获取字符串 s 的第一个或最后一个字母,将其从 s 中删除,并将其添加到自己的字符串中的开头。直到字符串 s 为空,操作结束,谁最后得到的字符串字典序小,谁获胜。假设每个人都执行最优操作,那么最终的结局是 Alice 获胜,还是 Bob 获胜, 或者是平局(字符串相同)?
题解:
定义表示:只考虑区间[l,r],先手的那个人在最优策略下执行完所有操作后结局是胜(用1表示),平局(用0表示),输(用-1表示)。
转移无非就4种情况:
对于区间[l,r]
- ALice选l,Bob选l+1或r
- Alice选r,Bob选l或r-1
先手固定后,后手的人有选择权,它会尽量的拿dp值小的
对于先手来说,一定选择dp值大的方案
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define YES {puts("YES");return;}
#define NO {puts("NO");return ;}
using namespace std;
const int maxn=3e5+101;
const int MOD=1e9+7;
const int inf=2147483647;
const double pi=acos(-1);
const double eps=1e-12;
ll read(){
ll x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
int dp[2002][2002];
string s;
int cal(int l,int r,int x /*Alice*/,int y){
if(dp[l][r])return dp[l][r];
if(s[x]>s[y])return -1;
if(s[x]==s[y])return 0;
return 1;
}
void solve(){
cin>>s;int n=s.length();
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)dp[i][j]=0;
for(int i=0;i<n-1;i++){
if(s[i]!=s[i+1])dp[i][i+1]=1;
}
for(int len=4;len<=n;len+=2){
for(int i=0;i<n;i++){
int j=len+i-1;if(j>=n)break;
int val1=inf,val2=inf;
//先手取i,后手选i+1或j
val1=min(cal(i+1,j-1,i,j),cal(i+2,j,i,i+1));
//先手取j,后手取i或j-1
val2=min(cal(i+1,j-1,j,i),cal(i,j-2,j,j-1));
dp[i][j]=max(val1,val2);
}
}
if(dp[0][n-1]==1)puts("Alice");
else if(dp[0][n-1]==0)puts("Draw");
else puts("Bob");
return ;
}
int main(){
int t=read();
while(t--)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】