再次垫底
开场发现A好像看过原题,是个单调栈,但是发现不太会做。
然后看B发现好像是模板题。
C也不太会做。
回头想A,可以过中线分治后合法的点集就是单调栈。
如果按照y降序排,则单调栈只有插入操作,容易维护。
左边的限制想了一想可以线段树+二分。
写完后就过了样例,然而手出的样例过不了。
手出的样例过了后,写了个暴力对拍拍出了错。
调了很久才拍上。。。。。
然后看B,B也是很久没调出来,暴力都没时间写。。。。。
交上去,A题被卡成暴力分,果然人傻常数大。
然而我在LibreOJ上过了,就算100分吧
最后只有100分。。。。。
总结:问题出在B的点分治树好久没写过了,忘记了线段树要开2n个,所以没写出来。
要复习一下点分治树的写法。
题解:
A:用经典的过中线分治。
先离散化。
一个左下端点合法的右上端点构成了单调栈中的单调点集:这个点集从左上到右下,(x)坐标单调递增,(y)坐标单调递减。
按照(y)坐标倒序排序所有点,顺序枚举所有左边点,把右边(y)坐标大于当前点的右边点插入单调栈
然而我们要考虑中线左边的限制。
事实上,我们发现这个限制可以用线段树维护。
建立线段树,维护区间最小值,初始所有元素为(inf)。
扫到第(i)个点时,先查询把([x,md])的最小值(v)。
发现当前点向上到(v-1)都是合法的。
由于单调栈中(x)递增,所以在单调栈上合法区间是连续的,可以二分。
然后把(x)位置的值修改成中点。
时间复杂度(O(nlog_2^2n))
不知道为什么跑的这么慢,但是在LibreOJ上过了
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200010
#pragma GCC optimize(3)
int n,x[N],y[N],st[N],tp,ans;
struct no{
int x,y;
}a[N];
struct st{
int a[N*5];
void mod(int o,int l,int r,int x,int y){
if(l==r){
a[o]=y;
return;
}
int md=(l+r)/2;
if(x<=md)
mod(o*2,l,md,x,y);
else
mod(o*2+1,md+1,r,x,y);
a[o]=min(a[o*2],a[o*2+1]);
}
int qu(int o,int l,int r,int x,int y){
if(r<x||y<l)
return 1e9;
if(x<=l&&r<=y)
return a[o];
int md=(l+r)/2;
return min(qu(o*2,l,md,x,y),qu(o*2+1,md+1,r,x,y));
}
}ss;
int operator <(no x,no y){
return x.y>y.y;
}
vector<no>v;
void fz(int l,int r,vector<no>v){
if(l>=r)
return;
int md=(l+r)/2;
vector<no>v1,v2;
for(int i=0;i<v.size();i++){
no x=v[i];
if(x.x<=md)
v1.push_back(x);
else
v2.push_back(x);
}
tp=0;
int j=0;
for(int i=0;i<v1.size();i++){
no x=v1[i];
while(j!=v2.size()&&v2[j].y>x.y){
while(tp&&v2[st[tp]].x>v2[j].x)
tp--;
st[++tp]=j;
j++;
}
int v=ss.qu(1,1,n,x.x,md),l=1,r=tp,po=tp+1;
while(l<=r){
int md=(l+r)/2;
if(v2[st[md]].y<=v){
po=md;
r=md-1;
}
else
l=md+1;
}
ans+=tp-po+1;
ss.mod(1,1,n,x.x,x.y);
}
for(int i=0;i<v1.size();i++){
no x=v1[i];
ss.mod(1,1,n,x.x,1e9);
}
fz(l,md,v1);
fz(md+1,r,v2);
}
signed main(){
freopen("scarecrows.in","r",stdin);
freopen("scarecrows.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&a[i].x,&a[i].y);
for(int i=1;i<=n;i++)
ss.mod(1,1,n,i,1e9);
for(int i=1;i<=n;i++){
x[i]=a[i].x;
y[i]=a[i].y;
}
sort(x+1,x+n+1);
sort(y+1,y+n+1);
for(int i=1;i<=n;i++){
a[i].x=lower_bound(x+1,x+n+1,a[i].x)-x;
a[i].y=lower_bound(y+1,y+n+1,a[i].y)-y;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
v.push_back(a[i]);
fz(1,n,v);
printf("%lld",ans);
}
B:模板题
显然二分,问题转化成判定和一个点距离(leq d)的数是否(>val)
这显然可以用点分治树解决,在每个节点上维护线段树即可。
#include<bits/stdc++.h>
using namespace std;
#define N 80010
int n,h[N],w[N],v[N],nxt[N],ec,tp[N],sz[N],p[N],f[N],d[N],di[N],rt[N],lc[N*20],rc[N*20],ss[N*20],rr,ms[N],vi[N],ps,ff[N],ct;
void add(int x,int y,int z){
v[++ec]=y;
w[ec]=z;
nxt[ec]=h[x];
h[x]=ec;
}
void d1(int x,int fa){
f[x]=fa;
sz[x]=1;
for(int i=h[x];i;i=nxt[i])
if(v[i]!=fa){
di[v[i]]=di[x]+w[i];
d[v[i]]=d[x]+1;
d1(v[i],x);
sz[x]+=sz[v[i]];
if(sz[v[i]]>sz[p[x]])
p[x]=v[i];
}
}
void d2(int x,int fa,int t){
tp[x]=t;
if(p[x])
d2(p[x],x,t);
for(int i=h[x];i;i=nxt[i])
if(v[i]!=fa&&v[i]!=p[x])
d2(v[i],x,v[i]);
}
int ll(int x,int y){
while(tp[x]!=tp[y]){
if(d[tp[x]]>d[tp[y]])
x=f[tp[x]];
else
y=f[tp[y]];
}
if(d[x]<d[y])
return x;
return y;
}
int dd(int x,int y){
return di[x]+di[y]-2*di[ll(x,y)];
}
void mod(int &o,int l,int r,int x,int y){
if(!o)
o=++ct;
ss[o]+=y;
if(l==r){
return;
}
int md=(l+r)/2;
if(x<=md)
mod(lc[o],l,md,x,y);
else
mod(rc[o],md+1,r,x,y);
}
int qq(int o,int l,int r,int x,int y){
if(r<x||y<l)
return 0;
if(x<=l&&r<=y)
return ss[o];
int md=(l+r)/2;
return qq(lc[o],l,md,x,y)+qq(rc[o],md+1,r,x,y);
}
void d4(int x,int fa){
sz[x]=1;
ms[x]=0;
for(int i=h[x];i;i=nxt[i])
if(!vi[v[i]]&&v[i]!=fa){
d4(v[i],x);
sz[x]+=sz[v[i]];
}
}
void gr(int x,int fa){
for(int i=h[x];i;i=nxt[i])
if(!vi[v[i]]&&v[i]!=fa){
gr(v[i],x);
ms[x]=max(ms[x],sz[v[i]]);
}
ms[x]=max(ms[x],ps-sz[x]);
if(ms[rr]>ms[x])
rr=x;
}
void d3(int x){
vi[x]=1;
for(int i=h[x];i;i=nxt[i])
if(!vi[v[i]]){
rr=0;
d4(v[i],x);
ps=sz[v[i]];
gr(v[i],x);
ff[rr]=x;
d3(rr);
}
}
void md(int x){
mod(rt[x],0,1e8,0,1);
int z=x;
while(ff[z]){
int d=dd(x,ff[z]);
mod(rt[ff[z]],0,1e8,d,1);
mod(rt[z+n],0,1e8,d,1);
z=ff[z];
}
}
int qu(int x,int va){
int y=x,ans=qq(rt[x],0,1e8,0,va);
while(ff[y]){
int d=dd(x,ff[y]);
if(va>=d)
ans+=qq(rt[ff[y]],0,1e8,0,va-d)-qq(rt[y+n],0,1e8,0,va-d);
y=ff[y];
}
return ans;
}
signed main(){
freopen("treekth.in","r",stdin);
freopen("treekth.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
ps=n;
d1(1,0);
d2(1,0,1);
memset(sz,0,sizeof(sz));
ms[0]=1e9;
d4(1,0);
gr(1,0);
d3(rr);
for(int i=1;i<=n;i++)
md(i);
for(int i=1;i<=n;i++){
int k;
scanf("%d",&k);
int l=0,r=1e8,ans=0;
while(l<=r){
int md=(l+r)/2;
if(qu(i,md)>=k){
ans=md;
r=md-1;
}
else
l=md+1;
}
printf("%d
",ans);
}
}