Ynoi 做题记录

P5069「Ynoi2015」纵使日薄西山

题目
首先注意到一个结论

i i i 处进行 ( a i − 1 , a i , a i + 1 ) (a_{i-1},a_i,a_{i+1}) (ai1,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}) (ai1,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=0get(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值