20260412 紫题训练
P2497 [SDOI2012] 基站建设
设点 \(i\) 直接收到点 \(j\) 信号的最小代价为 \(\sqrt{x}\),由两圆相切的条件可知:
DP,设 \(f_i\) 为基站 \(i\) 收到信号的最小代价。
转移式:
将 \(\min\) 里面的式子实际上是斜率为 \(\dfrac{1}{2\sqrt{r_j}}\),截距为 \(f_j-\dfrac{x_j}{2\sqrt{r_j}}\) 的直线在 \(x=x_i\) 处的值。
可以使用李超线段树优化,由于值域较大要动态开点,时间复杂度 \(\mathcal O(n\log n)\):
#include<bits/stdc++.h>
#define N 500005
using namespace std;
using db=double;
using ll=long long;
ll m,p[N];
int n,k,v[N],r[N];
db f[N],ans=INFINITY;
class SGT{
#define v(i) tr[i].val
#define l(i) tr[i].lch
#define r(i) tr[i].rch
private:
struct Seg{
db a,b;
db get(int x){return a*x+b;}
};
struct node{
Seg val;int lch,rch;
node(){val={0,INFINITY},lch=rch=0;}
}tr[N*20];int rt,idx;
static bool cmp(Seg x,Seg y,int k){
return x.get(k)>y.get(k);
}
void ins(Seg k,int &x,ll l=0,ll r=1e12){
if(!x) x=++idx;
int mid=l+r>>1;
if(cmp(v(x),k,mid)) swap(v(x),k);
if(l==r) return;
if(cmp(v(x),k,l)) ins(k,l(x),l,mid);
if(cmp(v(x),k,r)) ins(k,r(x),mid+1,r);
}
db query(int p,int &x,ll l=0,ll r=1e12){
if(!x) return INFINITY;
if(l==r) return v(x).get(p);int mid=l+r>>1;
if(p<=mid) return min(v(x).get(p),query(p,l(x),l,mid));
return min(v(x).get(p),query(p,r(x),mid+1,r));
}
public:
void ins(Seg x){ins(x,rt);}
db query(ll x){return query(x,rt);}
#undef v
#undef l
#undef r
}tr;
int main(){
scanf("%d%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld%d%d",p+i,r+i,v+i);
tr.ins({0.5/sqrt(r[1]),(f[1]=v[1])-p[1]/(2*sqrt(r[1]))});
for(int i=2;i<=n;i++)
f[i]=tr.query(p[i])+v[i],
tr.ins({0.5/sqrt(r[i]),f[i]-p[i]/(2*sqrt(r[i]))});
for(int i=1;i<=n;i++) if(p[i]+r[i]>=m) ans=min(ans,f[i]);
printf("%.3f",ans);
return 0;
}
P3714 [BJOI2017] 树的难题
点分治,设当前分治中心为 \(x\)。
记 \(x\) 到 \(y\) 的路径权值为 \(w_y\),\(x\) 与 \(y\) 的距离为 \(d_y\)。
若不考虑分治中心处的重复计算,用线段树维护最大值。
线段树下标为 \(i\) 的位置存储 \(\max_{d_y=i} w_y\),若不存在这样的 \(y\) 则为 \(-\infty\)。
这样对于每个递归到的节点 \(y\),拿 \(\max([\max(l-d_y,0),r-d_y])+w_y\) 尝试更新答案。
接下来考虑如何处理分治中心处的重复计算。
按颜色排序,相同的颜色一次性处理。
开两棵线段树记为 tr1,tr2。
tr1 与上面线段树相同,但要求是从 \(x\) 过去的颜色与当前处理颜色不同。
tr2 与上面线段树相同,但要求是从 \(x\) 过去的颜色与当前处理颜色相同。
处理颜色 \(i\) 的时候,用 \(\operatorname{tr1max}([\max(l-d_y,0),r-d_y])+w_y\) 和 \(\operatorname{tr1max}([\max(l-d_y,0),r-d_y])+w_y-c_i\) 尝试更新答案。
#include<bits/stdc++.h>
#define N 200005
using namespace std;
using ll=long long;
const ll inf=0x3f3f3f3f3f3f3f3fll;
struct edge{
int x,c;
bool operator<(const edge &t){
return c<t.c;
}
};
bitset<N>vis;
vector<edge>s[N];
int siz[N];ll ans=-inf;
vector<pair<int,ll>>tmp;
int n,m,L,R,a[N],rt,Min;
class SGT{
#define l(i) ((i)<<1)
#define r(i) ((i)<<1|1)
private:ll tr[N<<2];
public:
SGT(){memset(tr,-0x3f,sizeof tr);}
void upd(int p,ll v,int x=1,int l=0,int r=n){
if(l==r) return tr[x]=max(tr[x],v),void();
int mid=l+r>>1;
if(p<=mid) upd(p,v,l(x),l,mid);
else upd(p,v,r(x),mid+1,r);
tr[x]=max(tr[l(x)],tr[r(x)]);
}
void reset(int p,int x=1,int l=0,int r=n){
if(l==r) return tr[x]=-inf,void();
int mid=l+r>>1;
if(p<=mid) reset(p,l(x),l,mid);
else reset(p,r(x),mid+1,r);
tr[x]=max(tr[l(x)],tr[r(x)]);
}
ll query(int ql,int qr,int x=1,int l=0,int r=n){
if(ql<=l&&qr>=r) return tr[x];
int mid=l+r>>1;ll res=-inf;
if(ql<=mid) res=max(res,query(ql,qr,l(x),l,mid));
if(qr>mid) res=max(res,query(ql,qr,r(x),mid+1,r));
return res;
}
#undef l
#undef r
}tr1,tr2;
void Find(int x,int tot,int fa=0){
int mx=0;siz[x]=1;
for(auto p:s[x])
if(p.x^fa&&!vis[p.x])
Find(p.x,tot,x),
siz[x]+=siz[p.x],
mx=max(mx,siz[p.x]);
mx=max(mx,tot-siz[x]);
if(mx<Min) Min=mx,rt=x;
}
void updroot(int x,int tot){Min=INT_MAX,Find(x,tot),Find(rt,tot);}
void upd(int x,int fa,int v,int lst,int d=1){
if(vis[x]) return;tmp.emplace_back(d,v),tr2.upd(d,v);
for(auto p:s[x]) if(p.x^fa) upd(p.x,x,v+(p.c^lst?a[p.c]:0),p.c,d+1);
}
void calc(int x,int fa,ll v,int c,int lst,int d=1){
if(vis[x]||d>R) return;
ans=max({ans,tr1.query(max(L-d,0),R-d)+v,tr2.query(max(L-d,0),R-d)+v-c});
for(auto p:s[x]) if(p.x^fa) calc(p.x,x,v+(p.c^lst?a[p.c]:0),c,p.c,d+1);
}
void solve(int x){
vector<int>arr;vector<edge>son;
int lst=0;vis[x]=true,tmp.clear();
for(auto p:s[x]) if(!vis[p.x])
son.emplace_back(p);
sort(son.begin(),son.end());
for(auto p:son){
if(p.c^lst){
for(auto p:tmp)
tr2.reset(p.first),
tr1.upd(p.first,p.second),
arr.emplace_back(p.first);
tmp.clear(),lst=p.c;
}
calc(p.x,x,a[p.c],a[p.c],p.c);
upd(p.x,x,a[p.c],p.c);
}
for(auto p:arr) tr1.reset(p);
for(auto p:tmp) tr2.reset(p.first);
for(auto p:s[x]) if(!vis[p.x])
updroot(p.x,siz[p.x]),solve(rt);
}
int main(){
tr1.upd(0,0);
scanf("%d%d%d%d",&n,&m,&L,&R);
for(int i=1;i<=m;i++) scanf("%d",a+i);
for(int i=1,u,v,w;i<n;i++)
scanf("%d%d%d",&u,&v,&w),
s[u].push_back({v,w}),
s[v].push_back({u,w});
updroot(1,n),solve(rt),printf("%lld",ans);
return 0;
}
P4567 [AHOI2006] 文本编辑器
光标的位置显然好维护,只需要支持区间插入,区间删除,区间翻转,单点查询。
类似文艺平衡树用 FHQ Treap 维护区间,区间翻转与那题相同,交换左右儿子并打懒标记。
区间插入:在操作的位置裂开,按左边+插入的字符串+右边的顺序合并。
区间删除:在区间的左右端点裂开,再将区间的左右两边合并起来。
单点查询:将左右两边裂开获得询问位置,记得合并回去。
#include<bits/stdc++.h>
#define N 2100000
#define l(i) tr[i].lch
#define r(i) tr[i].rch
#define v(i) tr[i].val
#define s(i) tr[i].siz
#define p(i) tr[i].pri
#define f(i) tr[i].flag
using namespace std;
mt19937 Rand(20120412);
struct node{
bool flag;char val;
int lch,rch,siz,pri;
node(){}
node(int x){
lch=rch=flag=0;
val=x,siz=1,pri=Rand();
}
}tr[N];
string s;
int n,m,rt,idx;
void up(int x){if(x) s(x)=s(l(x))+s(r(x))+1;}
void down(int x){
if(!f(x)) return;
f(l(x))^=1,f(r(x))^=1;
swap(l(x),r(x)),f(x)=false;
}
void split(int x,int &l,int &r,int v){
if(!x) return l=r=0,void();
down(x);
if(v<=s(l(x))) r=x,split(l(x),l,l(r),v);
else l=x,split(r(x),r(l),r,v-s(l(x))-1);
up(x);
}
void merge(int &x,int l,int r){
if(!l||!r) return x=l+r,void();
if(p(l)<p(r)) down(x=l),merge(r(x),r(l),r);
else down(x=r),merge(l(x),l,l(r));
up(x);
}
void rev(int l,int r){
int x,y,z;
split(rt,x,y,r);
split(x,x,z,l-1);
f(z)^=1;
merge(x,x,z);
merge(rt,x,y);
}
void del(int l,int r){
int x,y,z;
split(rt,x,y,r);
split(x,x,z,l-1);
merge(rt,x,y);
}
void ins(int p,string s){
int x,y;split(rt,x,y,p);
for(auto p:s) tr[++idx]=p,merge(x,x,idx);
merge(rt,x,y);
}
void print(int p){
int x,y,z;
split(rt,x,y,p),split(x,x,z,p-1);
putchar(v(z));v(z)^'\n'?putchar('\n'):0;
merge(x,x,z),merge(rt,x,y);
}
int main(){
scanf("%d",&n);
for(int ptr=0;n--;){
cin>>s;int k;
if(s=="Move") scanf("%d",&k),ptr=k;
if(s=="Insert"){
string tmp;
scanf("%d",&k),getchar();
while(k--) tmp+=getchar();
ins(ptr,tmp);
}
if(s=="Delete") scanf("%d",&k),del(ptr+1,ptr+k);
if(s=="Rotate") scanf("%d",&k),rev(ptr+1,ptr+k);
if(s=="Get") ptr==idx?putchar('\n'):(print(ptr+1),0);
if(s=="Prev") ptr--;
if(s=="Next") ptr++;
}
return 0;
}

浙公网安备 33010602011771号