0221模拟赛(N≡N)
\(~~~~\) 我只能膜拜氮老师!
「CTSC2015」日程管理
题意
\(~~~~\) \(n\) 次操作,每次在 \(T\) 时间内加入一个截止日期 \(t\) ,价值为 \(p\) 的任务,或删除一个已有的任务。若完成每个任务需要一天,求每次操作后可以获得的最大价值。
\(~~~~\) \(1\leq T,n\leq 3\times 10^5\).
题解
\(~~~~\) 贪心结论:对于一个操作,能放到尽量后面的位置就放到尽量后面,因为前面的位置更宝贵。接下来都是围绕这个进行具体做法的讲解。
\(~~~~\) 首先先以时间为下标维护一个值,表示在这个时间及以前总共还可以放多少任务,把这个值记作 \(r_i\) 。
\(~~~~\) 对于加入操作:倘若在该时间内还有空余,也即 \([t,n]\) 的 \(r\) 最小值大于 \(0\)。 那么直接加入最后的那个空余(实际也就是 \(t\) 即可);否则我们将试图替换一个值来尝试加入:那么我们找到 \(t\) 之后第一个 \(r\) 为 \(0\) 的点 \(p\) ,则 \([1,p]\) 内的每一个任务都可以被替换。
\(~~~~\) 为此,我们还需要维护已经加入的任务当中最小价值,这需要再写一个线段树套 multiset
.而找 \(p\) 的过程可以在第一棵线段树上顺便维护最小值的来源直接找到。
\(~~~~\) 对于删除操作:我们如果删除的本身就没有被使用,那自然不需要再改变什么。
\(~~~~\) 如果它被使用了,那我们还是想办法找到一个替换的东西。具体来说我们找到全局最大的 \(r\) 为 \(0\) 的位置,那么这之后位置上没用的都可以新加入来填删除造成的空缺。
\(~~~~\) 为此,我们还需要维护没有加入的任务当中的最大价值,和上面第二个线段树写法类似。
\(~~~~\) 最后按分析合起来就好了。
代码
\(~~~~\) 下面注释是考场发电实录。
查看代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
template<typename T>void print(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
struct node{
int val,L,R;
node(){}
node(int Val,int l,int r){val=Val,L=l,R=r;}
};
node Merge(node a,node b)
{
node res;
if(a.val==b.val) res=node(a.val,a.L,b.R);
else if(a.val<b.val) res=a;
else res=b;
return res;
}
char op[10];
int N,brr[300005];
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
struct SegmentTree1{//维护每个位置前剩下多少位置可以放任务
int Minn[1200005],Tag[1200005],L[1200005],R[1200005];
void pushUp(int p)
{
Minn[p]=min(Minn[ls],Minn[rs]);
if(Minn[p]==Minn[ls]&&Minn[p]==Minn[rs]) L[p]=L[ls],R[p]=R[rs];
else if(Minn[p]==Minn[ls]) L[p]=L[ls],R[p]=R[ls];
else if(Minn[p]==Minn[rs]) L[p]=L[rs],R[p]=R[rs];
}
void pushDown(int p)
{
if(Tag[p])
{
Minn[ls]+=Tag[p]; Tag[ls]+=Tag[p];
Minn[rs]+=Tag[p]; Tag[rs]+=Tag[p];
Tag[p]=0;
return;
}
}
void Build(int p,int l,int r)
{
if(l==r)
{
Minn[p]=L[p]=R[p]=l;
return;
}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Add(int p,int l,int r,int lx,int rx,int val)
{
if(lx<=l&&r<=rx){Minn[p]+=val;Tag[p]+=val;return;}
int mid=(l+r)>>1; pushDown(p);
if(lx<=mid) Add(lson,lx,rx,val);
if(mid<rx) Add(rson,lx,rx,val);
pushUp(p);
}
node Query(int p,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return node(Minn[p],L[p],R[p]);
int mid=(l+r)>>1; pushDown(p);
if(lx<=mid&&mid<rx) return Merge(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
return Query(rson,lx,rx);
}
}Seg1;
struct SegmentTree2{//已选区间价值最小
multiset<int>S[300005];
PII Minn[1200005];//包含价值和位置
PII Merge(PII a,PII b)
{
if(a.fi<=b.fi) return a;
else return b;
}
void pushUp(int p){Minn[p]=Merge(Minn[ls],Minn[rs]);}
void Build(int p,int l,int r)
{
if(l==r)
{
Minn[p]=mp(1e9,l);
return;
}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Append(int p,int l,int r,int aim,int val)
{
if(l==r)
{
S[l].insert(val);
Minn[p].fi=min(Minn[p].fi,val);
return;
}
int mid=(l+r)>>1;
if(aim<=mid) Append(lson,aim,val);
else Append(rson,aim,val);
pushUp(p);
}
void Delete(int p,int l,int r,int aim,int val)
{
if(l==r)
{
S[l].erase(S[l].lower_bound(val));
if(S[l].empty()) Minn[p].fi=1e9;
else Minn[p].fi=*S[l].begin();
return;
}
int mid=(l+r)>>1;
if(aim<=mid) Delete(lson,aim,val);
else Delete(rson,aim,val);
pushUp(p);
}
PII Query(int p,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return Minn[p];
int mid=(l+r)>>1;
if(lx<=mid&&mid<rx) return Merge(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
if(mid<rx) return Query(rson,lx,rx);
assert(0); return mp(-114,-514);
}
bool IsInc(int p,int l,int r,int aim,int val)
{
if(l==r) return S[l].count(val);
int mid=(l+r)>>1;
if(aim<=mid) return IsInc(lson,aim,val);
else return IsInc(rson,aim,val);
}
}Seg2;
struct SegmentTree3{//没有用的最大值
multiset<int>S[300005];
PII Maxn[1200005];//包含价值和位置
PII Merge(PII a,PII b)
{
if(a.first>b.first) return a;
else return b;
}
void pushUp(int p){Maxn[p]=Merge(Maxn[ls],Maxn[rs]);}
void Build(int p,int l,int r)
{
if(l==r)
{
Maxn[p]=mp(-1e9,l);
return;
}
int mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void Append(int p,int l,int r,int aim,int val)
{
if(l==r)
{
S[l].insert(val);
Maxn[p].fi=max(Maxn[p].fi,val);
return;
}
int mid=(l+r)>>1;
if(aim<=mid) Append(lson,aim,val);
if(mid<aim) Append(rson,aim,val);
pushUp(p);
}
void Delete(int p,int l,int r,int aim,int val)
{
if(l==r)
{
S[l].erase(S[l].lower_bound(val));
if(S[l].empty()) Maxn[p].fi=-1e9;
else Maxn[p].fi=*prev(S[l].end());
return;
}
int mid=(l+r)>>1;
if(aim<=mid) Delete(lson,aim,val);
if(mid<aim) Delete(rson,aim,val);
pushUp(p);
}
PII Query(int p,int l,int r,int lx,int rx)
{
if(lx<=l&&r<=rx) return Maxn[p];
int mid=(l+r)>>1;
if(lx<=mid&&mid<rx) return Merge(Query(lson,lx,rx),Query(rson,lx,rx));
if(lx<=mid) return Query(lson,lx,rx);
if(mid<rx) return Query(rson,lx,rx);
assert(0); return mp(-1919,-810);
}
}Seg3;
#undef ls
#undef rs
#undef lson
#undef rson
int main() {
// freopen("task.in","r",stdin);
// freopen("task.out","w",stdout);
int n,m,t,p;read(n);read(m);
ll Ans=0;
Seg1.Build(1,1,n); Seg2.Build(1,1,n); Seg3.Build(1,1,n);
while(m--)
{
// cerr<<m<<endl;
// if(m==299875)
// {
// puts("???");
// }
// if(m==299718)
// {
// puts("???");
// }
scanf("%s",op+1); read(t); read(p);
if(op[1]=='A')
{
node res=Seg1.Query(1,1,n,t,n);
if(res.val>0)
Ans+=p,Seg1.Add(1,1,n,t,n,-1),Seg2.Append(1,1,n,t,p);
else
{
int pos=res.L;
PII Tmp=Seg2.Query(1,1,n,1,pos);
if(Tmp.fi<p)
{
Ans+=p; Ans-=Tmp.first;
Seg2.Delete(1,1,n,Tmp.se,Tmp.fi); Seg2.Append(1,1,n,t,p);
Seg1.Add(1,1,n,Tmp.second,n,1); Seg1.Add(1,1,n,t,n,-1);
Seg3.Append(1,1,n,Tmp.se,Tmp.fi);
}
else Seg3.Append(1,1,n,t,p);
}
}
else
{
if(!Seg2.IsInc(1,1,n,t,p))
Seg3.Delete(1,1,n,t,p);
else
{
Ans-=p;
Seg2.Delete(1,1,n,t,p); Seg1.Add(1,1,n,t,n,1);
node Tmp=Seg1.Query(1,1,n,1,n);
int pos;
if(Tmp.val<=0) pos=Tmp.R;
else pos=0;
if(pos!=n)
{
PII Temp=Seg3.Query(1,1,n,pos+1,n);
if(Temp.fi>0)
{
Ans+=Temp.fi;
Seg3.Delete(1,1,n,Temp.se,Temp.fi);
Seg2.Append(1,1,n,Temp.se,Temp.fi);
Seg1.Add(1,1,n,Temp.se,n,-1);
}
}
}
}
print(Ans);putchar('\n');
}
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
先用了一个小时进行心理建设(摆烂
首先加入一个东西的时候,如果有空闲时间那直接加入即可
否则看能不能从已经选了的里面选一个最小的来替掉
删除一个东西的时候如果删了个没选的那没影响
否则从剩下的最大的当中找一个来替掉
以时间(离散化) 为下标的线段树,记录第 i 天及以前可以放多少东西
加入的时候就看那个位置是否可以,线段树上二分找到最右非0位置,加入那直接 [i,n] 区间-1
等等最右非0好像就是t啊
阿巴阿巴阿巴
找替换: 在区间内找最小价值
说不定不一定要选区间之内/jk
找到这个点到后面最小的为0的值 , 那么在这之前的都可以被替换
所以还需要维护区间价值最小
怎么没有不带删的子任务啊,组题人你紫菜吧(无慈悲
删除任务:
若我删了一个没用的任务,那太好了赶紧去死
否则你**谁啊,地球没你照样转,你不来有的是人来,我们还要找一个没有被用的东西加入
找到最大的为0的位置,认为0位置一定为0,那么这之后所有位置上没用的都可以滚过来填坑
所以我们我要维护没有用的区间最大价值
然后后两个线段树套 multiset 。。。
组题人我祝你福无双至祸不单行福如沙漠寿比蜉蝣
线段树,斯哈斯哈/se 线段树,我的线段树/se
没有你我怎么活啊,线段树/ll
爱你孤身走暗巷 ~ 爱你不跪的模样 ~
爱你对峙过绝望 ~ 不肯哭一场 ~
线段树你怎么回事呜呜呜,线段树你不能啊,不能
*/
「SNOI2020」区间的和
题意
\(~~~~\) 长为 \(n\) 的整数数列 \(a_1,a_2,\dots,a_n\) ,对其进行 \(q\) 次操作,每次操作为:区间取 \(\max\) 或 查询区间最大子段和。
\(~~~~\) \(1\leq n\leq 10^5,1\leq q\leq 2\times 10^5,|a_i|,|x|\leq 10^9\).
题解
\(~~~~\) 考场(包括省选原题数据)做法:对每个区间除了维护最大子段和需要的四个元素之外还维护一个最小值,修改时暴力往下直到叶子,若区间最小值大于将修改的值直接返回。
\(~~~~\) 然后,它过了!!!
\(~~~~\) 当然要卡还是很好卡,造奇数位为 \(+\infty\),偶数位为 \(-\infty\) 的数据,取 \(\max\) 的时候取尽量长的区间慢慢把值加上去。
\(~~~~\) 所以还是得讲正解啊:考虑区间取 \(\max\) 这种东西都用吉司机线段树,那这题我们也类似地来考虑维护最大和次大。
\(~~~~\) 那么我们的四个变量就可以用 KTT 的方法,维护一次函数:\(ls+b\) ,这里 \(l\) 是会变化的数的个数,在这里也就是区间最小值的数量。
\(~~~~\) 那么每个结点的 \(x\) 的含义:最小值增加 \(x\) 发生变化。
\(~~~~\) 同时再注意一下两侧最小值需要统一的问题。
\(~~~~\) 讲的可能很不清楚QAQ
代码
查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
struct Func{
ll k,b;
Func(){}
Func(ll K,ll B){k=K,b=B;}
Func operator +(const Func a){return Func(k+a.k,b+a.b);}
void Add(ll v){b+=k*v;}
};
Func F(Func a,ll x){return Func(a.k*x,a.b);}
Func Fun(Func a,Func b,ll &x)
{
if(a.k<b.k||(a.k==b.k&&a.b<b.b)) swap(a,b);
if(a.b>=b.b) return a;
x=min(x,(b.b-a.b)/(a.k-b.k));
return b;
}
struct node{
Func L,R,Sum,Ans;
ll x,x2,t;
node(){}
node(Func l,Func r,Func s,Func a,ll X,ll X2,ll T){L=l,R=r,Sum=s,Ans=a,x=X,x2=X2,t=T;}
void Add(ll v)
{
t-=v; x+=v;
L.Add(v); R.Add(v); Sum.Add(v); Ans.Add(v);
}
};
node operator +(node a,node b)
{
node res;
res.x=min(a.x,b.x);
bool A=(a.x==res.x),B=(b.x==res.x);
res.x2=min(A?a.x2:a.x,B?b.x2:b.x);
res.t=res.x2-res.x-1;
if(A) res.t=min(res.t,a.t);
if(B) res.t=min(res.t,b.t);
res.Sum=F(a.Sum,A)+F(b.Sum,B);
res.L=Fun(F(a.L,A),F(a.Sum,A)+F(b.L,B),res.t);
res.R=Fun(F(b.R,B),F(b.Sum,B)+F(a.R,A),res.t);
res.Ans=Fun(Fun(F(a.Ans,A),F(b.Ans,B),res.t),F(a.R,A)+F(b.L,B),res.t);
return res;
}
ll arr[300005];
struct SegmentTree{
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
node tr[1200005];ll Tag[1200005];
void pushUp(ll p){tr[p]=tr[ls]+tr[rs];}
void Build(ll p,ll l,ll r)
{
if(l==r)
{
Func res=Func(1,arr[l]);
tr[p]=node(res,res,res,res,res.b,1e13,1e13);
return;
}
ll mid=(l+r)>>1;
Build(lson); Build(rson);
pushUp(p);
}
void pushTag(ll p,ll val){Tag[p]+=val;tr[p].Add(val);}
void pushDown(ll p)
{
if(!Tag[p]) return;
if(Tag[p])
{
bool A=tr[ls].x<=tr[rs].x,B=tr[ls].x>=tr[rs].x;
if(A) pushTag(ls,Tag[p]);
if(B) pushTag(rs,Tag[p]);
Tag[p]=0;
}
}
void Modify(ll p,ll l,ll r,ll lx,ll rx,ll val)
{
if(val<=tr[p].x) return;
if(lx<=l&&r<=rx&&val-tr[p].x<=tr[p].t) {pushTag(p,val-tr[p].x);return;}
ll mid=(l+r)>>1; pushDown(p);
if(lx<=mid) Modify(lson,lx,rx,val);
if(mid<rx) Modify(rson,lx,rx,val);
pushUp(p);
}
node Query(ll p,ll l,ll r,ll lx,ll rx)
{
if(lx<=l&&r<=rx) return tr[p];
ll mid=(l+r)>>1; pushDown(p);
if(lx<=mid&&mid<rx) return Query(lson,lx,rx)+Query(rson,lx,rx);
if(lx<=mid) return Query(lson,lx,rx);
return Query(rson,lx,rx);
}
#undef ls
#undef rs
#undef lson
#undef rson
}Seg;
int main() {
// freopen("sum.in","r",stdin);
// freopen("sum.out","w",stdout);
ll n,m,op,l,r,x;read(n);read(m);
for(int i=1;i<=n;i++) read(arr[i]);
Seg.Build(1,1,n);
while(m--)
{
read(op);read(l);read(r);
if(!op) read(x),Seg.Modify(1,1,n,l,r,x);
else printf("%lld\n",max(0ll,Seg.Query(1,1,n,l,r).Ans.b));
}
return 0;
}
/*
瑶草一何碧,春入武陵溪。溪上桃花无数,花上有黄鹂。我欲穿花寻路,直入白云深处,浩气展虹霓。只恐花深里,红露湿人衣。
坐玉石,欹玉枕。拂金徽。谪仙何处,无人伴我白螺杯。我为灵芝仙草,不为朱唇丹脸,长啸亦何为。醉舞下山去,明月逐人归。
*/
「CF1528F」 AmShZ Farm
\(~~~~\) 没补,或许很难。
「JSOI2019」神经网络
题意
\(~~~~\) \(m\) 棵树,把两个来自不同的树的结点连起来,求最终有多少从 \(1\) 开始的哈密顿回路。
\(~~~~\) \(1\leq \sum n_i\leq 5000\).
题解
\(~~~~\) 对每棵树写一个生成函数最后统一卷起来即可。
\(~~~~\) 没有代码,因为没写。