I 爆炸的符卡洋洋洒洒(dp)
题意:
一共 n 张符卡,每张符卡有消耗 ai,威力 bi。
问,保证消耗之和为 k 的倍数的情况下,最大威力为多少? 1 ≤ n, k ≤ 1000
思路:
dp
f[i, j]
表示:前i个位置,ai之和%k 为 j 时,最大 bi 和。
对于每个位置,看当前位置选上之后对答案有没有更新。
for(int i=1;i<=n;i++)
for(int j=0;j<k;j++)
f[i, (j+ai)%k] = max(f[i-1, (j+ai)%k], f[i, j]+b[i]).
初始化:
for(int i=1;i<k;i++) f[0, i] = -1e9;
因为是取max,所以一开始第0个位置的状态都赋值为无穷小。(除了 f[0, 0]
)
Code:
const int N = 2010, mod = 1e9+7;
int T, n, m, k;
int a[N], b[N];
int f[N][N];
signed main(){
Ios;
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
for(int j=1;j<k;j++) f[0][j] = -1e9;
for(int i=1;i<=n;i++)
{
for(int j=0;j<k;j++)
{
f[i][(j+a[i])%k] = max(f[i-1][(j+a[i])%k], f[i-1][j]+b[i]);
}
}
if(f[n][0]<=0) cout<<-1;
else cout<<f[n][0];
return 0;
}
R
题意:
给出长度为n的字符串,问一共有多少个连续子串,满足:
子串包含至少 k 个 'R'
字符,且不能包含 'P'
字符。
1 ≤ n ≤ 200000,1 ≤ k ≤ 20;
思路:
遍历每个 R 的位置:
找到从当前位置开始,第 k 个 ‘R’ 的位置 x,第一个 ‘P’ 的位置 y。
如果 y<x 的话,说明不存在连续的子串包含k个字符,不包含’P’字符;
否则,从x到y中的所有位置都可以作为右端点。
为了避免重复计算,左端点为上一个’R’的位置到当前位置,但是仍然不能包含’P’,所以就是当前位置之前的最后一个’P’或’R’的位置到当前位置的位置都可以作为左端点。
因为是从前往后走,所以找当前位置后面的第一个’p’的位置是一直往后走的,可以用双指针维护;
同理,上一个’P’和’R’的位置也可以这样维护。
Code:
const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], b[N];
char c[N];
signed main(){
Ios;
cin>>n>>k;
int cnt1=0, cnt2=0;
for(int i=1;i<=n;i++){
cin>>c[i];
if(c[i]=='R') a[++cnt1]=i, mp[i]=cnt1;
if(c[i]=='P') b[++cnt2]=i;
}
b[++cnt2] = n+1;
int np=0, fp1=0, fp2=0, ans=0, z=0;
for(int i=1;i<=n;i++)
{
if(c[i]!='R') continue;
int t = mp[i]+k-1;
if(t>cnt1) continue;
int x = a[t]; //最后一个R所在的位置
while(b[np]<i) np++;
int y = b[np]; //大于当前位置的第一个P所在位置
if(y<x) continue;
//z: 左边界
while(fp1+1<=cnt1 && a[fp1+1]<i) fp1++, z=max(z,a[fp1]);
while(fp2+1<=cnt2 && b[fp2+1]<i) fp2++, z=max(z,b[fp2]);
ans += (i-z)*(y-x);
}
cout<<ans;
return 0;
}