2023/2/8 考试总结

T1.P2569 [SCOI2010]股票交易

  • 单调队列优化 \(\mathtt{DP}\)

  • 设计状态 \(f_{i,j}\) 表示在第 \(i\) 天,手上有 \(j\) 张股票时的最大收益。

  • 状态转移分以下几种情况:

    • 才开始购入股票。\(f_{i,j}=-j\times ap_i\)
    • 从前一个状态原地转换过来。\(f_{i,j}=\max(f_{i,j},f_{i-1,j})\)
    • 再次购入股票。\(f_{i,j}=\max(f_{i,j},f_{i-W-1,j-k}-k\times ap_i)\)
    • 卖出股票。\(f_{i,j}=\max(f_{i,j},f_{i-W-1,j+k}+k\times bp_i)\)
  • 对于最后两种转移,使用单调队列优化。

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=2e3+10;
const int Inf=0x3f;

int T,mp,W;
int ap[N],bp[N],as[N],bs[N];
int f[N][N];
int q[N];

int main(){
	T=read(),mp=read(),W=read();
	for(int i=1;i<=T;++i)
		ap[i]=read(),bp[i]=read(),as[i]=read(),bs[i]=read();
	memset(f,-Inf,sizeof(f));
	int l,r;
	for(int i=1;i<=T;++i){
		for(int j=0;j<=as[i];++j)
			f[i][j]=-j*ap[i];
		for(int j=0;j<=mp;++j)
			f[i][j]=max(f[i][j],f[i-1][j]);
		if(i<=W) continue;
		l=1,r=0;
		for(int j=0;j<=mp;++j){
			while(l<=r && q[l]<j-as[i])
				++l;
			while(l<=r && f[i-W-1][q[r]]+q[r]*ap[i]<=f[i-W-1][j]+j*ap[i])
				--r;
			q[++r]=j;
			if(l<=r)
				f[i][j]=max(f[i][j],f[i-W-1][q[l]]+q[l]*ap[i]-j*ap[i]);
		}
		l=1,r=0;
		for(int j=mp;j>-1;--j){
			while(l<=r && q[l]>j+bs[i])
				++l;
			while(l<=r && f[i-W-1][q[r]]+q[r]*bp[i]<=f[i-W-1][j]+j*bp[i])
				--r;
			q[++r]=j;
			if(l<=r)
				f[i][j]=max(f[i][j],f[i-W-1][q[l]]+q[l]*bp[i]-j*bp[i]);
		}
	}
	int ans=0;
	for(int i=0;i<=mp;++i)
		ans=max(ans,f[T][i]);
	printf("%d",ans);
	return 0;
}

T2.P4331 [BalticOI 2004]Sequence 数字序列

  • 左偏树/可并堆;

  • 前置性质:
    序列 \(a'\) 指序列 \(a'_i=a_i-i\)\(b\) 序列指答案序列 \(-i\)

    • 如果序列 \(a'\) 是一个单调不减的序列,那么显然 \(b\) 序列直接和 \(a\) 序列相等时答案有最小值;
    • 如果序列 \(a'\) 是一个单调递减的序列,\(b\) 序列取 \(a'\) 中位数。
  • 问题转化为将原序列分为许多个小的单调序列,然后要求维护序列中位数。
    从第一个数开始往后一个一个加数,如果满足单调递增就合并,同时维护当前单调区间的左右端点。
    左偏树直接维护中位数。

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

#define ll long long

const int N=1e6+10;

int n,rt[N];
ll a[N],b[N];
int son[N][2];
int cnt=0;

struct memr{
	ll val;
	int l,r,siz,dist;
}tr[N];

#define ls(i) son[i][0]
#define rs(i) son[i][1]

int Merge(int x,int y){
	if(!x || !y) return x+y;
	if(a[x]<a[y] || (a[x]==a[y] && x>y))
		swap(x,y);
	rs(x)=Merge(rs(x),y);
	if(tr[ls(x)].dist<tr[rs(x)].dist)
		swap(ls(x),rs(x));
	tr[x].dist=tr[rs(x)].dist+1;
	return x;
}

int main(){
	n=read();
	tr[0].dist=-1;
	for(int i=1;i<=n;++i)
		a[i]=read()-i;
	for(int i=1;i<=n;++i){
		tr[++cnt]=(memr){a[i],i,i,1,0};
		rt[cnt]=i;
		while(cnt>1 && tr[cnt].val<tr[cnt-1].val){
			--cnt;
			rt[cnt]=Merge(rt[cnt],rt[cnt+1]);
			tr[cnt].siz+=tr[cnt+1].siz;
			tr[cnt].r=tr[cnt+1].r;
			while(tr[cnt].siz*2>tr[cnt].r-tr[cnt].l+2){
				--tr[cnt].siz;
				rt[cnt]=Merge(ls(rt[cnt]),rs(rt[cnt]));
			}
			tr[cnt].val=a[rt[cnt]];
		}
	}
	ll ans=0;
	for(int i=1;i<=cnt;++i){
		for(int j=tr[i].l;j<=tr[i].r;++j){
			b[j]=tr[i].val;
			ans+=abs(0ll+a[j]-b[j]);
		}
	}
	printf("%lld\n",ans);
	for(int i=1;i<=n;++i)
		printf("%lld ",b[i]+i);
	return 0;
}

T3.P2173 [ZJOI2012]网络

  • 但凡我 Find 后面加了 Splay,或者 O2 力度再大一点,我就当场 A 了

  • 纪念一下第一个当场写对的 LCT 板子

  • 很显然的 \(\mathtt{LCT}\)。对每个颜色都建一棵树,然后记录在每种颜色中每个点的度,维护最大值。

  • 一个坑点是如果修改边的颜色时,这条边本来就是这个颜色,应输出 Success. 而不是 Error x.
    排除了上面这种情况后,判断是否形成环只需要判一下两个点是否已经联通即可。

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=1e4+10;

int n,m,C,k;
int val[N];

struct memr{
	int son[2];
	int mx;
	int fa,fl,d;
	#define ls(i) tr[o][i].son[0]
	#define rs(i) tr[o][i].son[1]
}tr[11][N];

#define isson(i) (ls(tr[o][i].fa)==i || rs(tr[o][i].fa)==i)
#define g(i) (i==rs(tr[o][i].fa))

void pushup(int o,int x){
	tr[o][x].mx=max(val[x],max(tr[o][ls(x)].mx,tr[o][rs(x)].mx));
	return ;
}

void reverse(int o,int x){
	swap(ls(x),rs(x));
	tr[o][x].fl^=1;
	return ;
}

void pushdown(int o,int x){
	if(tr[o][x].fl){
		reverse(o,ls(x));
		reverse(o,rs(x));
		tr[o][x].fl=0;
	}
	return ;
}

void rotate(int o,int x){
	int y=tr[o][x].fa;
	int z=tr[o][y].fa,f=g(x);
	if(isson(y))
		tr[o][z].son[g(y)]=x;
	tr[o][x].fa=z;
	tr[o][y].son[f]=tr[o][x].son[f^1];
	tr[o][tr[o][x].son[f^1]].fa=y;
	tr[o][x].son[f^1]=y;
	tr[o][y].fa=x;
	pushup(o,y),pushup(o,x);
	return ;
}

void Splay(int o,int x){
	stack<int>s;
	s.push(x);
	int p=x;
	while(isson(p)){
		p=tr[o][p].fa;
		s.push(p);
	}
	while(s.size()){
		pushdown(o,s.top());
		s.pop();
	}
	for(int i;i=tr[o][x].fa,isson(x);rotate(o,x),i=tr[o][x].fa)
		if(isson(i))
			rotate(o,(g(x)==g(i))?i:x);
	pushup(o,x);
	return ;
}

void Access(int o,int x){
	for(int i=0;x;i=x,x=tr[o][x].fa){
		Splay(o,x);
		rs(x)=i;
		pushup(o,x);
	}
	return ;
}

void make_root(int o,int x){
	Access(o,x);
	Splay(o,x);
	reverse(o,x);
	return ;
}

void Split(int o,int x,int y){
	make_root(o,x);
	Access(o,y);
	Splay(o,y);
	return ;
}

int Find(int o,int x){
	Access(o,x);
	Splay(o,x);
	pushdown(o,x);
	while(ls(x)){
		x=ls(x);
		pushdown(o,x);
	}
	Splay(o,x);
	return x;
}

void Link(int o,int x,int y){
	make_root(o,x);
	tr[o][x].fa=y;
	++tr[o][x].d,++tr[o][y].d;
	return ;
}

void Cut(int o,int x,int y){
	Split(o,x,y);
	if(ls(y)==x && !rs(x))
		ls(y)=tr[o][x].fa=0;
	--tr[o][x].d,--tr[o][y].d;
	return ;
}

map<pair<int,int>,int>col;

int main(){
//	freopen("T3.in","r",stdin);
//	freopen("T3.ans","w",stdout);
	n=read(),m=read(),C=read(),k=read();
	int c;
	for(int i=1;i<=n;++i){
		val[i]=read();
		for(int j=1;j<=C;++j)
			tr[j][i].mx=val[i],tr[j][i].d=0;
	}
	int u,v;
	for(int i=1;i<=m;++i){
		u=read(),v=read(),c=read()+1;
		if(u>v) swap(u,v);
		col[make_pair(u,v)]=c;
		Link(c,u,v);
	}
	int opt;
	while(k--){
		opt=read(),u=read(),v=read();
		if(!opt){
			val[u]=v;
			for(int i=1;i<=C;++i){
				Split(i,u,u);
				tr[i][u].mx=v;
			}
			continue;
		}
		else if(opt==1){
			c=read();
			++c;
			if(u>v) swap(u,v);
			pair<int,int>x=make_pair(u,v);
			if(col[x]==c){
				puts("Success.");
				continue;
			}
			if(!col[x]){
				puts("No such edge.");
				continue;
			}
			else if((tr[c][u].d>=2 || tr[c][v].d>=2)){
				puts("Error 1.");
				continue;
			}
			else if(Find(c,u)==Find(c,v)){
				puts("Error 2.");
				continue;
			}
			Cut(col[x],u,v);
			Link(c,u,v);
			col[x]=c;
			puts("Success.");
		}
		else{
			c=read();
			if(Find(u+1,v)!=Find(u+1,c)){
				puts("-1");
				continue;
			}
			Split(u+1,v,c);
			printf("%d\n",tr[u+1][c].mx);
		}
	}
	return 0;
}

T4.P3976 [TJOI2015]旅游

  • \(\mathtt{LCT}\)/树链剖分

  • 已经写不来树链剖分了

  • 首先,让我们从 T3 那里粘一个正确的 LCT 板子过来。然后就不会了。
    由于她旅行是有一个方向的,所以并不是单纯地维护最小值最大值即可。我们考虑直接维护区间的最大差值。
    由于有方向性,所以我们需要维护两个差值:从左到右的最大差值 & 从右到左的最大差值。
    最大差值的产生有三种可能:左区间同向最大差、右区间同向最大差、跨区间差值。
    因此还需要维护区间的最大值和最小值,方便统计跨区间差值。
    \(\mathtt{Splay}\) 里区间翻转时两个值直接交换即可。

注意 \(tr_0\) 的初值,如果赋太大了可能导致一些溢出导致的错误。我就是因为这个 WA 了好几发

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=5e4+10;
const int Inf=0x3f3f3f3f;

int n,q;

struct memr{
	int son[2];
	int val,mx,mn,lmx,rmx;
	int fa,tg,fl;
	#define ls(i) tr[i].son[0]
	#define rs(i) tr[i].son[1]
}tr[N];

#define isson(i) (ls(tr[i].fa)==i || rs(tr[i].fa)==i)
#define g(i) (i==rs(tr[i].fa))

void pushup(int x){
	tr[x].mx=max(tr[x].val,max(tr[ls(x)].mx,tr[rs(x)].mx));
	tr[x].mn=min(tr[x].val,min(tr[ls(x)].mn,tr[rs(x)].mn));
	tr[x].lmx=max(tr[ls(x)].lmx,tr[rs(x)].lmx);
	tr[x].lmx=max(tr[x].lmx,max(tr[ls(x)].mx,tr[x].val)-min(tr[rs(x)].mn,tr[x].val));
	tr[x].rmx=max(tr[ls(x)].rmx,tr[rs(x)].rmx);
	tr[x].rmx=max(tr[x].rmx,max(tr[rs(x)].mx,tr[x].val)-min(tr[ls(x)].mn,tr[x].val));
	return ;
}

void reverse(int x){
	if(!x) return ;
	swap(ls(x),rs(x));
	swap(tr[x].lmx,tr[x].rmx);
	tr[x].fl^=1;
	return ;
}

void add(int x,int v){
	if(!x) return ;
	tr[x].val+=v;
	tr[x].mn+=v;
	tr[x].mx+=v;
	tr[x].tg+=v;
	return ;
}

void pushdown(int x){
	if(tr[x].tg){
		add(ls(x),tr[x].tg);
		add(rs(x),tr[x].tg);
		tr[x].tg=0;
	}
	if(tr[x].fl){
		reverse(ls(x));
		reverse(rs(x));
		tr[x].fl=0;
	}
	return ;
}

void rotate(int x){
	int y=tr[x].fa;
	int z=tr[y].fa,f=g(x);
	if(isson(y))
		tr[z].son[g(y)]=x;
	tr[x].fa=z;
	tr[y].son[f]=tr[x].son[f^1];
	tr[tr[x].son[f^1]].fa=y;
	tr[x].son[f^1]=y;
	tr[y].fa=x;
	pushup(y),pushup(x);
	return ;
}

void Splay(int x){
	stack<int>s;
	s.push(x);
	int p=x;
	while(isson(p)){
		p=tr[p].fa;
		s.push(p);
	}
	while(s.size()){
		pushdown(s.top());
		s.pop();
	}
	for(int i;i=tr[x].fa,isson(x);rotate(x),i=tr[x].fa)
		if(isson(i))
			rotate((g(x)==g(i))?i:x);
	pushup(x);
	return ;
}

void Access(int x){
	for(int i=0;x;i=x,x=tr[x].fa){
		Splay(x);
		rs(x)=i;
		pushup(x);
	}
	return ;
}

void make_root(int x){
	Access(x);
	Splay(x);
	reverse(x);
	return ;
}

void Split(int x,int y){
	make_root(x);
	Access(y);
	Splay(y);
	return ;
}

void Link(int x,int y){
	make_root(x);
	tr[x].fa=y;
	return ;
}

int main(){
//	freopen("P3976_2.in","r",stdin);
//	freopen("T4.ans","w",stdout);
	n=read();
	tr[0].mn=Inf;
	tr[0].mx=-Inf;
	for(int i=1;i<=n;++i)
		tr[i].mn=tr[i].mx=tr[i].val=read();
	int u,v;
	for(int i=1;i<n;++i){
		u=read(),v=read();
		Link(u,v);
	}
	int c;
	q=read();
	while(q--){
		u=read(),v=read(),c=read();
		Split(u,v);
		printf("%d\n",tr[v].rmx);
		add(v,c);
	}
	return 0;
}
posted @ 2023-02-08 15:07  Star_LIcsAy  阅读(25)  评论(0)    收藏  举报