P13272 NOI2025 D1T2 序列变换
感觉只有 *2500,但我连 NOI 都去不了所以说的话也全都是唐话。
令一次 \(a_i\leftarrow a_i-a_{i+1}\) 为 \(\leftarrow\),一次 \(a_{i+1}\leftarrow a_{i+1}-a_{i}\) 的操作为 $\rightarrow $。
容易发现我们可以对极长的形如 \(\rightarrow\rightarrow\rightarrow\leftarrow\leftarrow\leftarrow\) 的段进行转移。
定义 \(f_{i,0/1},g_{i,0/1}\) 为两个问题中,仅考虑前 \(i\) 个位置,第 \(i\) 个位置是否被操作覆盖的答案。
所以枚举极长段的结尾以及 \(\rightarrow\leftarrow\) 的分界点。需要注意的是因为要对本质不同的 \(D\) 序列进行计数,所以说要判断是否 \(\rightarrow\leftarrow\)分界点处的 \(a\) 会变成 \(0\) (也就是整个连续段的 \(a\) 变成 \(0\))。
\(O(n^3)\) 代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=5005,P=1e9+7;
using ll=long long;
const ll INF=1e18;
inline ll ksm(ll a,int b){
ll res=1;
while(b){
if(b&1)res=res*a%P;
a=a*a%P,b>>=1;
}
return res;
}
int n;
int a[N],b[N],c[N];
ll f[N][2];
int g[N][2];
inline void Max(ll &u,ll v){(u<v)&&(u=v);}
ll to[N][N];//=INF ->not valid
int nxt[N][2];
ll pr[N];
ll prd[N],ind[N];
inline ll gt(int l,int r){return pr[r]-pr[l-1];}
inline ll gm(int l,int r){return prd[r]*ind[l-1]%P;}
inline void Add(int &u,int v){u+=v;(u>=P)&&(u-=P);}
void solve(){
cin>>n;
prd[0]=ind[0]=1;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i],pr[i]=pr[i-1]+b[i];
for(int i=1;i<=n;i++)cin>>c[i],prd[i]=prd[i-1]*c[i]%P,ind[i]=ksm(prd[i],P-2);
for(int i=1;i<=n;i++){
f[i][0]=f[i][1]=-INF;
g[i][0]=g[i][1]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
to[i][j]=INF;
}
}
for(int i=1;i<=n;i++){
ll val=a[i];
nxt[i][0]=i;
to[i][i]=a[i];
for(int j=i+1;j<=n;j++){
if(val>a[j])break;
to[i][j]=val=a[j]-val;
nxt[i][0]=j;
if(val==0)break;
}
}
for(int i=1;i<=n;i++){
ll val=a[i];
nxt[i][1]=i;
for(int j=i-1;j>=1;j--){
if(val>=a[j])break;
nxt[i][1]=j;
to[i][j]=val=a[j]-val;
}
}
f[1][0]=0;
g[1][0]=1;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
Max(f[j][0],f[i][0]);
}
Max(f[i+1][0],f[i][1]);
Add(g[i+1][0],g[i][1]);Add(g[i+1][0],g[i][0]);
for(int j=i+1;j<=n;j++){
int l=nxt[j][1],r=nxt[i][0];
l=max(l,i),r=min(r,j);
if(l>r)continue;
ll v=g[i][0];
bool fl=0;
for(int k=l;k<=r;k++){
if(k==i){
Max(f[j][1],f[i][0]+(to[j][i]==0)*b[i]+gt(i+1,j));
if(to[j][i]==0)fl=1;
else Add(g[j][1],v*((to[j][i]==0)?c[i]:1)%P*gm(i+1,j)%P);
continue;
}
if(k==j){
Max(f[j][1],f[i][0]+(to[i][j]==0)*b[j]+gt(i,j-1));
if(to[i][j]==0)fl=1;
else Add(g[j][1],v*((to[i][j]==0)?c[j]:1)%P*gm(i,j-1)%P);
continue;
}
ll pt=to[i][k-1]+to[j][k+1];
if(pt>a[k])continue;
Max(f[j][1],f[i][0]+(pt==a[k])*b[k]+gt(i,k-1)+gt(k+1,j));
if(pt==a[k])fl=1;
else{
Add(g[j][1],v*gm(i,k-1)%P*gm(k+1,j)%P);
}
}
if(fl)Add(g[j][1],v*gm(i,j)%P);
}
}
cout<<max(f[n][0],f[n][1])<<" "<<(g[n][0]+g[n][1])%P<<"\n";
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int sub,T;
cin>>sub>>T;
while(T--)solve();
return 0;
}
然后就拿到了 \(75\) 分,因为 \(AB\) 性质的可能的极长 \(\leftarrow\) 或 $\rightarrow $ 长度都挺短的。
接着显然要把枚举 $\rightarrow \leftarrow $ 的这个步骤给砍掉,观察代码我们可以发现转移的条件只和 \(S=\sum_{p=i}^{j} (-1)^{p} a_p\) 是否 \(\ge 0\) 或者 \(-S\ge 0\) 有关,取决于枚举分界点的奇偶性。于是我们对分奇偶位置预处理一下就优化成 \(O(n^2)\) 了。
根本不会 FWT 啊,怎么有人认为 D2T2<D1T2 呢?

浙公网安备 33010602011771号