2024牛客暑期多校訓練營2
H.Instructions Substring
題意:
有一個字串序列,有WSAD四種操作,可以上下左右移動。可以選取一段連續的子序列,從(0,0)出發,經過連續子序列操作後可以經過點(x,y),問這樣的子序列有多少個
思路:
若一個子序列能夠實現到達點(x,y),那麼在這個子序列後面加任意字元都符合要求,因此只需要找到一個最短的合法子序列即可。一個完整的字串序列可以形成一個路徑,若截去前面一段子序列,形成的新路徑只需要將原有路徑加個字首和的偏移量即可。我們可以記錄下整個序列路徑中經過每個點時的下標;然後列舉子序列的起始下標,將(x,y)加上移動路徑字首和的偏移量,看原路徑是否經過這個點,經過這個點時的下標是否大於此時的起始下標,若滿足則可以二分得到最短合法子序列,然後加上這個貢獻n-右邊界+1
程式碼:
#include <bits/stdc++.h>
using namespace std ;
const int MAXN=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
#define endl "\n"
void solve(){
int n,x,y;
cin>>n>>x>>y;
string s;
cin>>s;
if(x==0&&y==0){
ll ans=n*(n+1)/2;
cout<<ans<<endl;
return ;
}
map<pair<int ,int >,vector<int > > mp;
int prex=0,prey=0;
for(int i=0;i<n;i++){
if(s[i]=='W'){
prey++;
}else if(s[i]=='S'){
prey--;
}else if(s[i]=='A'){
prex--;
}else{
prex++;
}
mp[{prex,prey}].push_back(i);
}
prex=0,prey=0;
ll ans=0;
for(int i=0;i<n;i++){
if(!mp[{x+prex,y+prey}].empty()){
int left=0,right=mp[{x+prex,y+prey}].size();
while(left<right){
int mid=(left+right)/2;
if(mp[{x+prex,y+prey}][mid]<i){
left=mid+1;
}else{
right=mid;
}
}
if(left<mp[{x+prex,y+prey}].size()&&mp[{x+prex,y+prey}][left]<n){
ans+=n-mp[{x+prex,y+prey}][left];
}
}
if(s[i]=='W'){
prey++;
}else if(s[i]=='S'){
prey--;
}else if(s[i]=='A'){
prex--;
}else{
prex++;
}
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
// cin>>t;
while(t--){
solve();
}
return 0;
}
I.Red Playing Cards
題意:
有2n張卡牌,1到n的所有數字都恰好出現2次。
每次操作,可以選擇相同數字的兩張牌,刪去這兩張牌之間的區間(包含這兩張牌),其貢獻為這個數字大小區間的牌數,問怎樣操作可以使貢獻最大
思路:
對於兩個區間,若兩個區間有重疊部分,則兩個區間只能二選一;若兩個區間是包含關係,則可以選擇先取裡面的小區間,再取外面的大區間;若兩個區間沒有任何重疊,則可以都選取。
區間的貢獻為“邊界數字大小*區間的牌數”,那麼可以看作這個區間每個數的貢獻為“邊界的數字大小”.對於兩個區間是包含關係的情況,可以知道若“小區間的數字大小”大於“大區間的數字大小”,那麼先取小區間再取大區間可以最大化貢獻,反之沒有意義。
因此我們可以先預處理出每個數字的左右邊界,從大到小遍歷這些數字,得出每個數字的區間的最大貢獻,最後考慮沒有重疊的區間的貢獻相加(增加0的區間來實現)。
\(f_i\)為數字i的區間的最大貢獻,\(dp_j\)為數字i區間內部的每張牌的貢獻的字首和。
狀態轉移方程為:
\(dp_j\)=\(dp_{j-1}\)+i
\(dp_{r[a[j]]}\)=\(dp_{j-1}\)+\(f_{a[j]}\) (如果\(j=l[a[j]]\)並且\(r[a[j]]<r[i]\))
程式碼:
#include <bits/stdc++.h>
using namespace std ;
const int MAXN=1e5+7;
const int INF = 0x3f3f3f3f;
typedef long long ll;
#define endl "\n"
void solve(){
int n;
cin>>n;
int a[2*n+1];
for(int i=1;i<=2*n;i++){
cin>>a[i];
}
vector<int > l(n+1,-1),r(n+1);
for(int i=1;i<=2*n;i++){
if(l[a[i]]==-1){
l[a[i]]=i;
}else{
r[a[i]]=i;
}
}
l[0]=0;
r[0]=2*n+1;
vector<int > f(n+1,0);
for(int i=n;i>=0;i--){
vector<int > dp(2*n+1,0);
for(int j=l[i]+1;j<r[i];j++){
dp[j]=max(dp[j],dp[j-1]+i);
if(j==l[a[j]]&&r[a[j]]<r[i]){
dp[r[a[j]]]=max(dp[r[a[j]]],dp[j-1]+f[a[j]]);
}
}
f[i]=2*i+dp[r[i]-1];
}
cout<<f[0]<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t=1;
// cin>>t;
while(t--){
solve();
}
return 0;
}