• 2021 第二轮省队集训 Day5


    A

    先考虑 (k=0) 怎么做:用 set 维护每个点的前驱和后继,用线段树维护区间内后继编号的最小值以及区间 (v) 的和。

    • 对于修改操作,它只会修改三个位置的后继:原数、原来的前驱、新前驱。直接在线段树里单点修改就行了。
    • 对于查询操作,由于 (k=0),所以只能选最长的一段颜色互不相同的一段。在线段树内二分这个段的长度即可。

    (k>0),那么修改的做法不变,查询的做法就是上述做法重复 (k) 次:每次找到第一个有重复颜色的位置,并更新这个颜色对应的最大值,然后继续找下一个位置。

    考场上写了个 (O(nk log^2 n)) 的做法,但实际上上述二分可以做到 (1log)

    代码($O(nklog n)$)
    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <set>
    #include <tuple>
    #include <vector>
    using namespace std;
    #define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
    #define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
    typedef long long ll;
    template<typename T> void Read(T &x){
    	x=0;int f=1,ch;
    	for(ch=getchar();!isdigit(ch);ch=getchar()) f=(ch=='-'?-1:1);
    	for(;isdigit(ch);ch=getchar()) x=x*10+(ch-48);
    	x*=f;
    }
    template<typename T,typename... Args> void Read(T &x,Args&... args){
    	Read(x);Read(args...);
    }
    const int N=2e5+5,Inf=0x3f3f3f3f;
    int n,m,c[N],v[N],nxt[N];
    set<int> st[N];
    #define ls(xx) ((xx)<<1)
    #define rs(xx) ((xx)<<1|1)
    struct Node{
    	int l,r,nxt,minp;ll vsum;
    }t[N<<2];
    void Pushup(int p){
    	if(t[ls(p)].nxt<t[rs(p)].nxt){
    		t[p].nxt=t[ls(p)].nxt,t[p].minp=t[ls(p)].minp;
    	}else{
    		t[p].nxt=t[rs(p)].nxt,t[p].minp=t[rs(p)].minp;
    	}
    	t[p].vsum=t[ls(p)].vsum+t[rs(p)].vsum;
    }
    void Build(int p,int l,int r){
    	t[p].l=l,t[p].r=r;
    	if(l==r){
    		t[p].nxt=nxt[l],t[p].vsum=v[l];
    		t[p].minp=l;
    		return;
    	}
    	int mid=(l+r)>>1;
    	Build(ls(p),l,mid);Build(rs(p),mid+1,r);
    	Pushup(p);
    }
    void Modify(int p,int pos,int nxt_,int val){
    	if(t[p].l==t[p].r){
    		t[p].nxt=nxt_;
    		if(~val) t[p].vsum=val;
    		return;
    	}
    	int mid=(t[p].l+t[p].r)>>1;
    	if(pos<=mid) Modify(ls(p),pos,nxt_,val);
    	else Modify(rs(p),pos,nxt_,val);
    	Pushup(p);
    }
    int QMinp(int p,int l,int r){
    	if(l<=t[p].l&&t[p].r<=r) return t[p].minp;
    	int mid=(t[p].l+t[p].r)>>1,minp=0;
    	if(l<=mid) minp=QMinp(ls(p),l,r);
    	if(r>mid){
    		int p_=QMinp(rs(p),l,r);
    		if(!minp||nxt[minp]>nxt[p_]) minp=p_;
    	}
    	return minp;
    }
    ll QSum(int p,int l,int r){
    	if(l<=t[p].l&&t[p].r<=r) return t[p].vsum;
    	int mid=(t[p].l+t[p].r)>>1;ll res=0;
    	if(l<=mid) res+=QSum(ls(p),l,r);
    	if(r>mid) res+=QSum(rs(p),l,r);
    	return res;
    }
    void Getp(int p,int l,int r,int *pos,int &cnt){
    	if(l<=t[p].l&&t[p].r<=r){
    		pos[++cnt]=p;return;
    	}
    	int mid=(t[p].l+t[p].r)>>1;
    	if(l<=mid) Getp(ls(p),l,r,pos,cnt);
    	if(r>mid) Getp(rs(p),l,r,pos,cnt);
    }
    int Search(int p,int mn){
    	if(t[p].l==t[p].r) return t[p].l;
    	if(min(mn,t[ls(p)].nxt)<=t[ls(p)].r) return Search(ls(p),mn);
    	else return Search(rs(p),min(mn,t[ls(p)].nxt));
    }
    int QMaxp(int s){
    	static int seg[105];
    	int len=0,rseg=1,mn=n+1,pre=n+1;Getp(1,s,n,seg,len);
    	for(;rseg<=len;++rseg){
    		mn=min(mn,t[seg[rseg]].nxt);
    		if(mn<=t[seg[rseg]].r) break;
    		pre=mn;
    	}
    	if(rseg<=len) return Search(seg[rseg],pre);
    	else return n+1;
    }
    ll mxans[N],tag[N];int tcnt=0;
    ll Query(int s,int k){
    	++tcnt;
    	int l=QMaxp(s);
    	ll res=QSum(1,s,l-1);
    	if(!k) return res;
    	vector<int> chg;
    	For(i,1,k){
    		if(l==n+1) break;
    		int p=QMinp(1,s,l);
    		if(tag[c[p]]!=tcnt){
    			tag[c[p]]=tcnt;
    			if(v[p]<v[l]) res+=v[l]-v[p],mxans[c[p]]=v[l];
    			else mxans[c[p]]=v[p];
    		}else{
    			if(mxans[c[p]]<v[l]) res+=v[l]-mxans[c[p]],mxans[c[p]]=v[l];
    		}
    		chg.push_back(p);
    		Modify(1,p,Inf,-1);
    		nxt[p]=Inf;
    		int oldl=l;
    		l=QMaxp(s);
    		if(oldl+1<=l-1) res+=QSum(1,oldl+1,l-1);
    	}
    	for(int i:chg){
    		int p=*++st[c[i]].find(i);
    		Modify(1,i,p,-1);nxt[i]=p;
    	}
    	return res;
    }
    int main(){
    	freopen("gp.in","r",stdin);
    	freopen("gp.out","w",stdout);
    	Read(n,m);
    	For(i,1,n) st[i].insert(n+1);
    	For(i,1,n){
    		Read(c[i],v[i]);
    		st[c[i]].insert(i);
    	}
    	For(i,1,n) nxt[i]=*++st[c[i]].find(i);
    	Build(1,1,n);
    	int op,x,col,val;
    	while(m--){
    		Read(op,x,col);
    		if(op==1){
    			Read(val);
    			auto it=st[c[x]].find(x);
    			it=st[c[x]].erase(st[c[x]].find(x));
    			if(it!=st[c[x]].begin()){
    				int nxt_=*it;int pre=*--it;
    				nxt[pre]=nxt_;
    				Modify(1,pre,nxt_,-1);
    			}
    			c[x]=col,v[x]=val;
    			it=st[c[x]].insert(x).first;
    			nxt[x]=*++it;
    			Modify(1,x,*it,val);
    			--it;
    			if(it!=st[c[x]].begin()){
    				int pre=*--it;nxt[pre]=x;
    				Modify(1,pre,x,-1);
    			}
    		}else{
    			printf("%lld
    ",Query(x,col));
    		}
    	}
    	return 0;
    }
    

    如何在某个区间内进行线段树二分

    首先,在线段树上找到那些可以拼凑出查询区间的那些区间,递归时可以指定递归顺序,来让这些区间有序。然后从左往右遍历这些区间,找到第一个不满足条件的区间,并在其中进行正常的线段树二分。时间复杂度 (1log)

    B

    对所有 (T_i) 建立 AC 自动机,于是它形成了一个有向图。在这张图上进行概率 dp,设 (f_{i}) 为从 (i) 到任意一个终末位置的期望步数。那么 (f_{i}=egin{cases}1+sumlimits_{(i,v)in E} p_{i o v}f_v & i ext{不是终末位置} \ 0 & i ext{是终末位置}end{cases})

    最暴力的做法就是对这 (nm) 个未知数进行高斯消元。时间复杂度 (O(n^3m^3))

    对它的优化可以用主元法。即,我们用一些未知数来表示其他所有的未知数,最后通过一些相等关系来列方程。

    主元法

    在一个 (n imes n) 的网格图中随机游走,从第 (1) 列中的任意一点开始,问走到第 (n) 列的期望步数。

    直接做需要 (n^2) 个未知数。考虑用未知数 (x_i) 表示从第 (x) 行第 (1) 列开始走的期望步数,(y_i) 表示从第 (y) 行第 (2) 列开始走的期望步数,那么 (x_i=dfrac{1}{3}(x_{i-1}+x_{i+1}+y_i)+1)。于是 (y_i) 可以用 (x_i,x_{i-1},x_{i+1}) 表示。后面的列同理。

    递推到最后一行的时候,它形成了 (n) 个关于 (x_i) 的方程,于是高斯消元即可。时间复杂度 (O(n^3))

    杂题 1

    首先,(n)([1,m]) 之间的随机整数的期望最大值是 (m-dfrac{m}{n})。因为 (m) 一定大于 (max{b_i}),所以从这个最大值开始枚举。对于每个枚举的 (m),有方程 (sum a_i + nkequiv sum b_i pmod m),所以 (k) 可以解出来。

    Written by Alan_Zhao
  • 相关阅读:
    Scala_模式匹配
    Scala_特质
    Scala_继承
    Scala_对象
    Scala_类
    Scala_关键字
    Scala_数据结构
    Scala_方法、函数、柯里化
    Scala_控制结构
    Scala_基本语法
  • 原文地址:https://www.cnblogs.com/alan-zhao-2007/p/sdptt-2021-2-day5.html
Copyright © 2020-2023  润新知