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;
}