Codeforces Round #721 (Div. 2)


B2. Palindrome Game (hard version)

题意:
a l i c e alice alice b o b bob bob轮流操作一个长度为 ( n ≤ 1000 ) (n\le 1000) (n1000) 01 01 01串, a l i c e alice alice先手,有两种操作:
1. 把一个 0 0 0变成 1 1 1,花费 1 1 1
2. 把当前非回文串翻转,花费 0 0 0元,但是上一步不能为翻转操作
求当串全为 1 1 1时,花费多的输掉,求最后的胜负情况

思路: d p dp dp(记忆化搜索)

状态表示: d p [ s a m ] [ d i f ] [ m i d ] [ r e v ] dp[sam][dif][mid][rev] dp[sam][dif][mid][rev],表示当前操作这个状态的人相比另一个人多多少钱

  1. s a m sam sam表示当前左右对称的0的个数
  2. d i f dif dif表示当前左右不对称的0的个数
  3. m i d mid mid表示串长度为奇数,并且中间数是否为 0 0 0
  4. r e v rev rev表示上一次操作是否为翻转操作

状态转移:

  1. 翻转操作: ( d i f > 0 & & r e v = 0 ) , a n s = − d f s ( s a m , d i f , m i d , 1 ) (dif>0 \&\&rev=0),ans=-dfs(sam,dif,mid,1) (dif>0&&rev=0),ans=dfs(sam,dif,mid,1),这里取负号是因为 d f s dfs dfs后是抛给对手的状态,对手比我们多,相当于我们比对手少,因此取负号
  2. 删除一个不对称的 0 0 0 : ( d i f > 0 ) , a n s = 1 − d f s ( s a m , d i f , m i d , 0 ) (dif>0),ans=1-dfs(sam,dif,mid,0) (dif>0),ans=1dfs(sam,dif,mid,0),删除一个 0 0 0需要花费 1 1 1
  3. 删除一个对称的 0 0 0: ( s a m > 0 ) , a n s = 1 − d f s ( s a m − 1 , d i f + 1 , m i d , 0 ) (sam>0),ans=1-dfs(sam-1,dif+1,mid,0) (sam>0),ans=1dfs(sam1,dif+1,mid,0),删除一个对称的 0 0 0,就会增加一个不对称的 0 0 0
  4. 删除中间的0: ( m i d > 0 ) , a n s = 1 − d f s ( s a m , d i f , 0 , 0 ) (mid>0),ans=1-dfs(sam,dif,0,0) (mid>0),ans=1dfs(sam,dif,0,0)
    每步操作都是最优,因此四种情况取 m i n min min

边界条件:
记忆化搜索初始化为 − 1 -1 1
d p [ 0 ] [ 0 ] [ 0 ] [ 0 ] = 0 dp[0][0][0][0]=0 dp[0][0][0][0]=0,表示当前为空串的情况

AC代码:

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define int ll
#define mst(a,x) memset(a,x,sizeof(a))
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define pb emplace_back
#define mp make_pair
#define endl '\n'
#define fi first
#define se second
#define ls u<<1
#define rs u<<1|1
typedef pair<int,int> PII;
typedef vector<int> VI;
mt19937 rnd(time(0));
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=1e3+10,M=2*N;

int n;
string s;
int sam,dif,mid,ans;
int dp[N][N][2][2];

int dfs(int sam,int dif,bool mid,bool rev){
    int &res=dp[sam][dif][mid][rev];
    if(res!=-1) return res;
    res=2e9;
    if(dif&&!rev) res=min(res,-dfs(sam,dif,mid,1));
    if(dif) res=min(res,1-dfs(sam,dif-1,mid,0));
    if(sam) res=min(res,1-dfs(sam-1,dif+1,mid,0));
    if(mid) res=min(res,1-dfs(sam,dif,0,0));
    return res;
}

void solve(){
    cin>>n>>s;
    sam=dif=mid=0;
    if(n&1&&s[n/2]=='0') mid=1;
    else mid=0;

    rep(i,0,n/2-1){
        if(s[i]==s[n-1-i]&&s[i]=='0') sam++;
        else if(s[i]=='0'||s[n-1-i]=='0') dif++;
    }
    ans=dfs(sam,dif,mid,0);
    if(ans>0) cout<<"BOB\n";
    else if(ans==0) cout<<"DRAW\n";
    else cout<<"ALICE\n";
}

signed main(){
    mst(dp,-1);
    dp[0][0][0][0]=0;
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;cin>>_;while(_--)
    solve();
    return 0;
}

C. Sequence Pair Weight

题意:
定义权值为序列中 i < j i<j i<j并且 a i = a j a_i=a_j ai=aj ( i , j ) (i,j) (i,j)对数
给定一个长度为 n ( 1 ≤ 1 0 5 ) n(1\le10^5) n(1105)的序列 a a a,求 a a a的所有连续子序列的权值和

思路:
a a a的值域很大,先进行离散化
如果存在 ( i , j ) (i,j) (i,j)符合条件,统计有多少子序列包括这对 ( i , j ) (i,j) (i,j),设子序列首尾为 l , r l,r l,r,则 1 ≤ l ≤ i 1\le l\le i 1li j ≤ r ≤ n j\le r\le n jrn时, a [ l ∼ r ] a[l\sim r] a[lr]包含 ( i , j ) (i,j) (i,j),这样的子序列有 i ∗ ( n − j + 1 ) i*(n-j+1) i(nj+1)

答案为: a n s = ∑ j = 1 n ( ( n − j + 1 ) ∗ ∑ i < j & a i = a j i ) ans=\sum_{j=1}^{n}((n-j+1)*\sum_{i<j\&a_i=a_j}i) ans=j=1n((nj+1)i<j&ai=aji)

我们可以从 1 ∼ n 1\sim n 1n枚举 j j j s u m [ x ] sum[x] sum[x]为小于 j j j并求数值 = x =x =x的下标和, a n s + = ( n − j + 1 ) ∗ s u m [ a j ] ans+=(n-j+1)*sum[a_j] ans+=(nj+1)sum[aj],然后 s u m [ a j ] + = j sum[a_j]+=j sum[aj]+=j

AC代码:

#include <bits/stdc++.h>
#include <algorithm>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define ll long long
#define int ll
#define mst(a,x) memset(a,x,sizeof(a))
#define SZ(x) ((int)(x).size())
#define ALL(x) (x).begin(),(x).end()
#define pb emplace_back
#define mp make_pair
#define endl '\n'
#define fi first
#define se second
#define ls u<<1
#define rs u<<1|1
typedef pair<int,int> PII;
typedef vector<int> VI;
mt19937 rnd(time(0));
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int N=1e5+10,M=2*N;

int n;
int a[N];
VI num;
int sum[N];

int getid(int x){
    return lower_bound(ALL(num),x)-num.begin()+1;
}

void solve(){
    num.clear();
    cin>>n;
    rep(i,1,n){
        cin>>a[i];
        num.pb(a[i]);
        sum[i]=0;
    }    
    sort(ALL(num));
    num.erase(unique(ALL(num)),num.end());

    int ans=0;
    rep(i,1,n){
        int x=getid(a[i]);
        ans+=(n-i+1)*sum[x];
        sum[x]+=i;
    }
    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int _;cin>>_;while(_--)
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值