P5069「Ynoi2015」纵使日薄西山
题目
首先注意到一个结论
在 i i i 处进行 ( a i − 1 , a i , a i + 1 ) (a_{i-1},a_i,a_{i+1}) (ai−1,ai,ai+1) 减一的操作一直进行到 a i = 0 a_i=0 ai=0,不会改变最终答案。
证明:在 i i i 处进行操作不会影响 ( a i − 1 , a i , a i + 1 ) (a_{i-1},a_i,a_{i+1}) (ai−1,ai,ai+1) 的大小关系。
所以只需要关注操作中心的集合就可以了。然后我们考虑用线段树维护。
为了方便处理,我们先将相邻两个数对应区间的答案作为线段树叶子结点对应的区间。然后在线段树上维护一个类似 dp 的数组 r e s [ 0 / 1 / 2 ] [ 0 / 1 / 2 ] res[0/1/2][0/1/2] res[0/1/2][0/1/2],表示区间最左端和最右端的数 没有被覆盖 / 被覆盖 / 是极大值。
然后类似 dp 地在线段树上合并。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#define int long long
#define lc note<<1
#define rc note<<1|1
using namespace std;
const int N=1e5+5;
int n,m,x,y;
int a[N];
inline int read()
{
int s=0,t=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')t=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s*t;
}
struct matrix
{
int cnt[5][5];
int val[5];
};
inline matrix operator + (matrix p,matrix q)
{
matrix res;
memset(res.cnt,-1,sizeof(res.cnt));
for(int i=0;i<=2;++i)
for(int j=0;j<=2;++j)
for(int k=0;k<=2;++k)
for(int l=0;l<=2;++l)
{
if(j+k<2) continue;
if(!j&&k==2&&!(p.val[2]<q.val[1])) continue;
if(j==2&&!k&&!(p.val[2]>=q.val[1])) continue;
if(p.cnt[i][j]==-1||q.cnt[k][l]==-1) continue;
if(res.cnt[i][l]!=-1)
res.cnt[i][l]=min(res.cnt[i][l],p.cnt[i][j]+q.cnt[k][l]);
else res.cnt[i][l]=p.cnt[i][j]+q.cnt[k][l];
}
res.val[1]=p.val[1];
res.val[2]=q.val[2];
return res;
}
struct Tree
{
int left,right;
matrix res;
}s[N<<2];
inline void push_up(int note){
s[note].res=s[lc].res+s[rc].res;}
inline void build(int note,int l,int r)
{
s[note].left=l;
s[note].right=r;
if(l==r)
{
int x=l*2-1;
int y=l*2;
s[note].res.val[1]=a[x];
s[note].res.val[2]=a[y];
memset(s[note].res.cnt,-1,sizeof(s[note].res.cnt));
s[note].res.cnt[0][0]=0;
if(a[x]>=a[y]) s[note].res.cnt[2][1]=a[x],s[note].res.cnt[0][2]=a[y];
if(a[x]<a[y]) s[note].res.cnt[1][2]=a[y],s[note].res.cnt[2][0]=a[x];
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
push_up(note);
return;
}
inline void modify(int note,int pos,int x,int y)
{
if(s[note].left==s[note].right)
{
s[note].res.val[x]=y;
int A=s[note].res.val[1];
int B=s[note].res.val[2];
memset(s[note].res.cnt,-1,sizeof(s[note].res.cnt));
s[note].res.cnt[0][0]=0;
if(A>=B) s[note].res.cnt[2][1]=A,s[note].res.cnt[0][2]=B;
if(A<B) s[note].res.cnt[1][2]=B,s[note].res.cnt[2][0]=A;
return;
}
int mid=(s[note].left+s[note].right)>>1;
if(pos<=mid) modify(lc,pos,x,y);
else modify(rc,pos,x,y);
push_up(note);
return;
}
inline int query()
{
int ans=1ll<<60;
for(int i=1;i<=2;++i)
for(int j=1;j<=2;++j)
if(s[1].res.cnt[i][j]!=-1) ans=min(ans,s[1].res.cnt[i][j]);
return ans;
}
signed main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
n=read();
for(int i=1;i<=n;++i) a[i]=read();
n=(n-1)/2+1;
build(1,1,n);
m=read();
while(m--)
{
x=read();
y=read();
modify(1,(x-1)/2+1,(x-1)%2+1,y);
printf("%lld\n",query());
}
return 0;
}
P4689「Ynoi2016」这是我自己的发明
前置
前置的做法是:
∑ x = 0 ∞ g e t ( l 1 , r 1 , x ) × g e t ( l 2 , r 2 , x ) \sum_{x=0}^\infty get(l_1,r_1,x)\times get(l_2,r_2,x) ∑x=0∞get(l1,r1,x)×get(l2,r2,x)
\text{}
= ∑ x = 0 ∞ ( g e t ( 1 , r 1 , x ) − g e t ( 1 , l 1 − 1 , x ) ) × ( g e t ( 1 , r 2 , x ) − g e t ( 1 , l 2 − 1 , x ) ) =\sum_{x=0}^\infty (get(1,r_1,x)-get(1,l_1-1,x))\times(get(1,r_2,x)-get(1,l_2-1,x)) =∑x=0∞(get(1,r1,x)−get(1,l1