B2. Palindrome Game (hard version)
题意:
a
l
i
c
e
alice
alice和
b
o
b
bob
bob轮流操作一个长度为
(
n
≤
1000
)
(n\le 1000)
(n≤1000)的
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],表示当前操作这个状态的人相比另一个人多多少钱
- s a m sam sam表示当前左右对称的0的个数
- d i f dif dif表示当前左右不对称的0的个数
- m i d mid mid表示串长度为奇数,并且中间数是否为 0 0 0
- r e v rev rev表示上一次操作是否为翻转操作
状态转移:
- 翻转操作: ( 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后是抛给对手的状态,对手比我们多,相当于我们比对手少,因此取负号
- 删除一个不对称的 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=1−dfs(sam,dif,mid,0),删除一个 0 0 0需要花费 1 1 1元
- 删除一个对称的 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=1−dfs(sam−1,dif+1,mid,0),删除一个对称的 0 0 0,就会增加一个不对称的 0 0 0
- 删除中间的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=1−dfs(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(1≤105)的序列
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
1≤l≤i且
j
≤
r
≤
n
j\le r\le n
j≤r≤n时,
a
[
l
∼
r
]
a[l\sim r]
a[l∼r]包含
(
i
,
j
)
(i,j)
(i,j),这样的子序列有
i
∗
(
n
−
j
+
1
)
i*(n-j+1)
i∗(n−j+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((n−j+1)∗∑i<j&ai=aji)
我们可以从 1 ∼ n 1\sim n 1∼n枚举 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+=(n−j+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;
}