第一天:
牛客多校第五场D Double Strings
链接:牛客第五次double strings
给定两个字符串A,B求长度相同的AB子串,使得子串的前面部分相等,且第一个不相等的位置B>A。求这样的子串个数。
思路:f[i][j]表示A的前i个字符与B的前j个字符能组成多少相等的子串。
自我理解:
比如说abcd,abdc,那样遍历到b的时候由于前面a是一样的,那当前的字串数量f[i][j]就会加上前面的字串数量+f[i-1][j-1],(也就是加上a时的数量),其他情况下就是加上大一点的那个,f[i][j]=((f[i-1][j]+f[i][j-1])%mod-f[i-1][j-1]+mod)%mod;比如说f[i-1][j]=5,f[i][j-1]=6,f[i-1][j-1]是两者重合的部分,那f[i][j]就会是大一点的那个(我猜的hhh)
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
char a[5005],b[5005];
int dp[5005][5005],f[5005][5005];
ll res=0;
const int mod=1e9+7;
int main(){
scanf("%s%s",a+1,b+1);
int n=strlen(a+1),m=strlen(b+1);
memset(f,0,sizeof(f));
f[0][0]=1;
for (int i=1; i<=m; i++) f[0][i]=1;
for (int i=1; i<=n; i++) f[i][0]=1;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
f[i][j]=((f[i-1][j]+f[i][j-1])%mod-f[i-1][j-1]+mod)%mod;
if (a[i]==b[j]){
f[i][j]=(f[i-1][j-1]+f[i][j])%mod;
}
}
}
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
if (a[i]<b[j]) dp[i][j]=(dp[i][j]+f[i-1][j-1])%mod;
}
}
printf("%lld",dp[n][m]);
return 0;
}
Rise in Price(杭电多校DP):
链接:Rise in Price
题意:从(1,1)走到(n,n),只能向下或者向右走,输入a数组(nn),然后输入b数组(nn),每个格子都有a和b,表示有a个钻石,可以让单价上涨b.
思路:dp[i][j][k]表示到达ij时所持有的宝石为k的时候可以卖的价钱,不难想到设两个状态f[i][j][x]和f[i][j][y],当x<y时,若f[i][j][x]<f[i][j][y]时,前者不可能是最优解,数量少且单价低的一定不是最优解,故剔除,剩下的可能的最优解与i,j上的a,b构成i,j位置上的最优解
官方题解:
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
typedef pair<long long, long long> PII;
typedef vector<PII> V;
typedef long long ll;
vector<PII> p[N][N];
PII tem[N * N * N]; //临时序列
int topz = 0;
ll a[N][N], b[N][N];
void check(PII x) {
while(topz && tem[topz].second <= x.second) topz --; //序列中的所有的first都要小于x的first,因为是first按升序排的,
//这句话也保证了序列中的second从大到小
if(topz == 0 || tem[topz].first < x.first) tem[++ topz] = x; //当x的first == 序列中的某个可能最优解的first的同时,
//x的second一定小于这个可能最优解的second,也就是说是一个非最优解
}
void merge(const V &x, const V &y, V &z) {
int sx = x.size(), sy = y.size();
int topx = 0, topy = 0;
topz = 0;
while(topx < sx && topy < sy)
check((x[topx].first < y[topy].first) ? x[topx ++] : y[topy ++]); //转移过来的可能最优解也是按first升序排列的
while(topx < sx) check(x[topx ++]);
while(topy < sy) check(y[topy ++]);
for(int i = 1; i <= topz; ++ i) z.push_back(tem[i]); //将筛完之后的可能最优解按宝石数量升序放入下一个状态中
}
void solve() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
scanf("%lld", &a[i][j]), p[i][j].clear();;
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
scanf("%lld", &b[i][j]);
p[1][1].push_back(make_pair(a[1][1], b[1][1]));
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j) {
if(i == 1 && j == 1) continue;
else if(i == 1)
p[i][j].push_back(p[i][j - 1][0]);//第一行和第一列的转移都之由一种情况
else if(j == 1)
p[i][j].push_back(p[i - 1][j][0]);
else
merge(p[i - 1][j], p[i][j - 1], p[i][j]);
for(int k = 0; k < p[i][j].size(); ++ k) p[i][j][k].first += a[i][j], p[i][j][k].second += b[i][j];
}
ll MAX = 0;
for(int i = 0; i < p[n][n].size(); ++ i) {
MAX = max(MAX, p[n][n][i].first * p[n][n][i].second); //从所有没有被提出的可能最优解当中选出真正的最优解
}
printf("%lld\n", MAX);
}
int main() {
int t;
scanf("%d", &t);
while(t --) {
solve();
}
return 0;
}
牛客第五场 K-King of Range
ST表+双指针
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<set>
#include<queue>
using namespace std;
#define ll long long
#define N 100005
int stmax[N][20],stmin[N][20],mn[N];
int a[N];
int t,q,n;
int x,y;
void init()
{
mn[0]=-1;
for (int i=1;i<=n;i++)
{
mn[i]=((i & (i-1))==0) ? mn[i-1]+1 : mn[i-1];
stmax[i][0]=stmin[i][0]=a[i];
}
for (int j=1;j<=mn[n];j++)
for (int i=1;i+(1<<j)-1<=n;i++)
{
stmax[i][j]=max(stmax[i][j-1],stmax[i+(1<<(j-1))][j-1]);
stmin[i][j]=min(stmin[i][j-1],stmin[i+(1<<(j-1))][j-1]);
}
}
int rmq_max(int L,int R)
{
int k=mn[R-L+1];
return max(stmax[L][k],stmax[R-(1<<k)+1][k]);
}
int rmq_min(int L,int R)
{
int k=mn[R-L+1];
return min(stmin[L][k],stmin[R-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
scanf("%d",&q);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
init();
while(q--){
int k;
scanf("%d",&k);
int cnt=0,l=0;
ll ans=0;
for(int i=2;i<=n;i++){
while(l<i&&rmq_max(l+1,i)-rmq_min(l+1,i)>k){
l++;
}
ans+=l;
}
printf("%lld\n",ans);
}
return 0;
}
杭电第八场
我的题解链接:T_T
AC代码:
#include<iostream>
#define LL long long
using namespace std;
const LL mod = 998244353;
const int N = 100005;
LL a1[N], a2[N];
int T, n, q;
LL lowbit(LL x) { return x & (-x); }
struct SegmentTree1 {
LL l, r, sum1, sum2, la, tg;
#define ls x<<1
#define rs x<<1|1
#define l(x) tree[x].l
#define r(x) tree[x].r
#define sum1(x) tree[x].sum1
#define sum2(x) tree[x].sum2
#define la(x) tree[x].la
#define tg(x) tree[x].tg
}tree[N << 2];
//向上是线段树合并
void pushup(int x) {
sum1(x) = (sum1(ls) + sum1(rs)) % mod;
sum2(x) = (sum2(ls) + sum2(rs)) % mod;
tg(x) = tg(ls) & tg(rs);//如果两个区间都是1的话就不继续递归下去了
}
void pushdown(int x) {
la(ls) = la(ls) * la(x) % mod;
la(rs) = la(rs) * la(x) % mod;
sum1(ls) = sum1(ls) * la(x) % mod;
sum1(rs) = sum1(rs) * la(x) % mod;
tg(ls) |= tg(x);
tg(rs) |= tg(x);
if (tg(ls))sum2(ls) = 0;
if (tg(rs))sum2(rs) = 0;
la(x) = 1;//标记
}
void build(int x,int l,int r) {
la(x) = 1, tg(x) = 0;
l(x)=l,r(x)=r;
if (l(x) == r(x)) {
sum1(x) = a1[l(x)], sum2(x) = a2[l(x)];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);
}
LL query(int x, int l, int r) {
if (l <= l(x) && r >= r(x)) {
return (sum1(x) + sum2(x)) % mod;
}
pushdown(x);
int mid = (l(x) + r(x)) >> 1;
LL ans = 0;
if (l <= mid)ans += query(ls, l, r);
if (r > mid)ans += query(rs, l, r);
ans%=mod;
return ans;
}
void up1(int x, int l, int r) {
if (l(x) == r(x)) {
if (sum2(x)) {
sum2(x) -= lowbit(sum2(x));
}
else {
sum1(x) = 0;
tg(x) = 1;
}
return;
}
pushdown(x);
int mid = (l(x) + r(x)) >> 1;
if (l <= mid && !tg(ls))up1(ls, l, r);
if (r > mid && !tg(rs))up1(rs, l, r);
pushup(x);
}
void up2(int x, int l, int r) {
if (l <= l(x) && r >= r(x)) {
sum1(x) = sum1(x) * 2 % mod;
la(x) = la(x) * 2 % mod;
return;
}
pushdown(x);
int mid = (l(x) + r(x)) >> 1;
if (l <= mid)up2(ls, l, r);
if (r > mid)up2(rs, l, r);
pushup(x);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
LL x;
scanf("%lld", &x);
for (int k = 30; k >= 0; k--) {
if ((1ll << k) <= x) {
a1[i] = (1ll << k);
a2[i] = x - a1[i];
break;
}
}
}
build(1,1,n);
scanf("%d", &q);
while (q--) {
int opt, l, r;
scanf("%d%d%d", &opt, &l, &r);
if (opt == 1) {
printf("%lld\n", query(1, l, r));
}
else if (opt == 2) {
up1(1, l, r);
}
else if (opt == 3) {
up2(1, l, r);
}
}
}
return 0;
}