zoukankan      html  css  js  c++  java
  • HNOI2018 转盘+线段树维护单调栈学习笔记

    题目链接

    题目简述

    给定一个环形数组.你在时间为(0)时在一个你自己选的位置,每个时刻可以往下走或不走.如果你走到(i)的时刻(leq T_i),那么可以把(i)标记.求把所有环上的点标记的最小时间.


    解析

    其实没有什么难的知识点.
    但是好神仙啊(QAQ)

    我们转化一下.假设时间(t)时在一个点,每次可以往前走或者不动.每个点会在(T_i)消失.求最小的(t).

    转化后怎么做呢?
    假设答案是(t),然后破环成链.
    这都没什么好说的.
    然后枚举一个点(iin (n,2*n]),那么走到点(j)的时间是(t-(i-j))
    对于(forall jin (i-n,i],t-(i-j)geq T_j)
    简单移个项可得(tgeq T_j-j+i)
    (a_i=T_i-i)
    (tgeq max{a_j}+i)
    (t=min_{i=n}^{2*n-1}{max_{j=i-n+1}^i{a_j}+i})
    再令(ileftarrow i+n-1)
    于是(t=min_{i=1}^{n}{max_{j=i}^{i+n-1}{a_j}+i}+n-1)
    考虑到(a_{k+n}<a_k)
    我们扩大一下范围,得到
    (t=min_{i=1}^{n}{max_{j=i}^{2*n}{a_j}+i}+n-1)
    对于一个(i),可以算它的最大后缀,但是好像并没有什么作用.

    考虑(j)的答案.
    我们分类讨论一下.

    • 如果(a_j)是最大后缀值,那么找到第一个(i),使得(a_i>a_j),贡献就是(a_j+i+1)
    • 如果(a_j)不是最大后缀值,那么找到它之后的第一个最大后缀值(k),答案就是(k)的答案.也就是说,(a_j)对答案没有贡献.

    这启发了我们要使用单调栈来维护这个答案.
    单调栈是从后往前单调增的.
    由于还要支持修改,因此用一棵线段树即可.
    其实这道题已经做完啦,接下来只是讲一下线段树维护单调栈的方法


    线段树如何维护单调栈呢?
    我们对于每个节点([L,R])(编号是(x)),都维护一个(Max)表示区间最大值,再维护一个(val)表示(min_{i=L}^{mid}{max_{j=i}^{R}{a_j}+i})
    可以发现这个(val)就是用单调栈的方式维护一个答案.
    我们令(query(v,x))表示在(x)节点加入一个权值为(v)的点之后这个节点的(val)(即答案).
    我们令(lc)表示(x)的左儿子,(rc)表示(x)的右儿子.

    • 如果(vleq Max_{rc}),那么右儿子的答案与左儿子取(min)就是最终的答案.由于此时(val_{x})就是维护左儿子的答案,因此(val_x=min(val_x,query(v,rc))).
    • 如果(v>Max_{rc}),相当于加入(v)之后所有的([mid+1,R])中原来在单调栈中的元素都被弹出了.因此直接返回(query(v,lc))即可.

    我们计算一下时间复杂度(下面认为(n,m)同阶).
    每次(query)的时间复杂度为(T(n)=T(frac{n}{2})+1=log n)
    (build)每次是(O(n*log n))的,每个节点都要调用一次(query),因此是(O(n*log^2 n))的.每次(modify)(log)的,然后再调用一次(query),也是(log^2)的.因此总时间复杂度就是(O(n*log^2 n))的.

    那么就完结撒花了!
    代码如下
    真的超级短

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<vector>
    #define N (200010)
    #define inf (0x7f7f7f7f)
    #define rg register int
    #define Label puts("NAIVE")
    #define spa print(' ')
    #define ent print('
    ')
    #define rand() (((rand())<<(15))^(rand()))
    typedef long double ld;
    typedef long long LL;
    typedef unsigned long long ull;
    using namespace std;
    inline char read(){
    	static const int IN_LEN=1000000;
    	static char buf[IN_LEN],*s,*t;
    	return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
    }
    template<class T>
    inline void read(T &x){
    	static bool iosig;
    	static char c;
    	for(iosig=false,c=read();!isdigit(c);c=read()){
    		if(c=='-')iosig=true;
    		if(c==-1)return;
    	}
    	for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
    	if(iosig)x=-x;
    }
    inline char readchar(){
    	static char c;
    	for(c=read();!isalpha(c);c=read())
    	if(c==-1)return 0;
    	return c;
    }
    const int OUT_LEN = 10000000;
    char obuf[OUT_LEN],*ooh=obuf;
    inline void print(char c) {
    	if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
    	*ooh++=c;
    }
    template<class T>
    inline void print(T x){
    	static int buf[30],cnt;
    	if(x==0)print('0');
    	else{
    		if(x<0)print('-'),x=-x;
    		for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
    		while(cnt)print((char)buf[cnt--]);
    	}
    }
    inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
    struct xds{
    	int l,r,mx,val;
    }a[N<<3];
    int n,m,w[N],pp,lans;
    int query(int v,int x){
    	if(a[x].l==a[x].r){
    		if(a[x].mx>v)return v+a[x].l;
    		else return inf;
    	}
    	if(a[x*2+1].mx>v)return min(query(v,x*2+1),a[x].val);
    	else return query(v,x*2);
    }
    void pushup(int x){
    	a[x].mx=max(a[x*2].mx,a[x*2+1].mx);
    	a[x].val=query(a[x*2+1].mx,x*2);
    }
    void build(int l,int r,int x){
    	a[x].l=l,a[x].r=r;
    	if(l==r){
    		a[x].mx=w[l]-l;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,x*2),build(mid+1,r,x*2+1);
    	pushup(x);
    }
    void modify(int k,int v,int x){
    	if(a[x].l==a[x].r){
    		a[x].mx=v-a[x].l;
    		return;
    	}
    	int mid=(a[x].l+a[x].r)>>1;
    	if(k<=mid)modify(k,v,x*2);
    	else modify(k,v,x*2+1);
    	pushup(x);
    }
    int main(){
    	read(n),read(m),read(pp);
    	for(int i=1;i<=n;i++)read(w[i]);
    	build(1,n,1);
    	print(lans=query(a[1].mx-n,1)+n),ent;
    	while(m--){
    		int x,y;
    		read(x),read(y);
    		if(pp)x^=lans,y^=lans;
    		modify(x,y,1);
    		print(lans=query(a[1].mx-n,1)+n),ent;
    	}
    	return flush(),0;
    }
    
  • 相关阅读:
    【ZOJ3195】Design the City-LCA算法
    【ZOJ3195】Design the City-LCA算法
    【POJ1330】Nearest Common Ancestors-LCA算法
    【POJ1330】Nearest Common Ancestors-LCA算法
    【POJ3237】Tree-树链剖分
    【POJ3237】Tree-树链剖分
    【SPOJ375】Query on a tree-树链剖分
    perl 回调必须是函数引用
    运行复制的ZooKeeper 部署
    运行复制的ZooKeeper 部署
  • 原文地址:https://www.cnblogs.com/Romeolong/p/10070823.html
Copyright © 2011-2022 走看看