2023 0809 模拟赛
\(T\ 1\)
题意简述:
- 有 \(n\) 条线段,定义线段异或值为它们并的长度减它们交的长度,在其中找到两条有公共点的线段,使得它们的异或值最大。
- 输入的第一行包括一个正整数 \(n\),表示线段的个数。接下来 \(n\) 行每行包括两个正整数 \(l\) , \(r\) ,表示线段的左右端点。
- 数据:
- 对于 \(20\%\) 的数据,满足 \(l\le 300,r\le 300,n\le 300\)
- 对于 \(40\%\) 的数据,满足 \(n\le 2000\)
- 对于另外 \(10\%\) 的数据,满足 \(l=1\)
- 对于 \(100\%\) 的数据,满足 \(1\le n\le 200000,1\le l \le r\le 10^{8}\)
题目解法:
想到线段树- 先考虑排个序,考虑以右端点为第一关键字()。
- 拿一棵线段树维护 \(1\) 到 \(10^{8}\) 的值域,然后依次插入每条线段(的端点)。
- 考虑两种情况:第一种情况为一条线段包含着另一条线段;第二种情况为两条线段相交。
- 设两条线段分别为 \(l_1,\ r_1\) 与 \(l_2,\ r_2\)
- 若线段相交,则答案为 \((r_1-r_2)+(l_1-l_2)\) ,整理得 \((l_1+r_1)-(l_2+r_2)\) 。(假设 \(1\) 线段在 \(2\) 线段的右边)
- 若线段包含,则答案为 \((r_1-l_1)+(r_2-l_2)\) 。(假设 \(1\) 线段包含 \(2\) 线段)
- 两种情况若同时统计答案可能有些困难)于是考虑分开统计)
- 已经以右端点排过序了,那么线段相交的情况中,当前线段视为在右边的线段,要找的最佳答案即为向左找到的所有与当前线段相交的线段中 \((l+r)\) 值最小的线段 \(\Longrightarrow\) 即可以用线段树维护辣。
具体,考虑将每个点的右端点插入到线段树当中,其值为 \((l+r)\) ,线段树维护一段内的 \(min\) 值。每统计一条线段的答案时,直接查询这条线段区间里面的最小值即可,再用当前线段的 \((l+r)\) 值减去 \(min\) 值。显然以右端点插入时,这条线段的区间里面所统计到的答案一定包含全部线段相交的答案。
但是,线段包含的也会被统计到,但手画一下即可知道线段包含这么统计,统计出来的答案比真正答案要小,所以并不影响。比如:相交是用当前线段(当前线段为 \(i\)) \(l_i+r_i-l_j-r_j\) ,又要求 \(l_j+r_j\) 最小,若包含则为 \((r_i-l_i)-(r_j-l_j)\) 显然以线段相交的方法所统计出来的线段包含答案偏小,因此没有影响。 - 相应,线段包含即可将线段的左端点插入线段树中,其值为 \(r-l\) ,维护另一个 \(min2\) 值。每统计一条线段的答案时,直接查询这条线段区间里的 \(min2\) 值即可。由于已经以右端点为第一关键字排序过了,查询时显然都是线段包含的答案。
- 算完当前线段的答案,线段树中插入当前线段的两个值即可)
- 时间复杂度 \(O(nlogn)\),空间 \(1e8\) 的线段数开不下,需要离散化一下即可。
CODE:
Click
#include<bits/stdc++.h>
using namespace std;
namespace Light_Tea{signed main();}
signed main(){return Light_Tea::main();}
namespace Light_Tea{
#define int long long
void rd(int &n){
n=0; int f=1; char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
n=(n<<3)+(n<<1)+ch-'0';
ch=getchar();
}
n*=f;
}
const int maxn=2e5+5,maxw=5e8;
int n,pos[maxn<<1];
struct line{
int l,r;
friend bool operator < (line x,line y){
return (x.r==y.r)?(x.l<y.l):(x.r<y.r);
}
}a[maxn];
struct Seg_Tree{
int minl,minl2;
}tr[maxn<<3];
#define lson(_) (_<<1)
#define rson(_) ((_<<1)|1)
//int cnt=1;
void pushup(int kk){
tr[kk].minl=min(tr[lson(kk)].minl,tr[rson(kk)].minl);
tr[kk].minl2=min(tr[lson(kk)].minl2,tr[rson(kk)].minl2);
}
void build(int ll,int rr,int kk){
if(ll==rr){
tr[kk].minl=tr[kk].minl2=maxw;
return ;
}
int mid=(ll+rr)>>1;
build(ll,mid,lson(kk));
build(mid+1,rr,rson(kk));
pushup(kk);
}
void update(int ll,int rr,int kk,int xx,int yy){
if(ll==rr){
tr[kk].minl=min(tr[kk].minl,yy);
return ;
}
int mid=(ll+rr)>>1;
if(xx<=mid) update(ll,mid,lson(kk),xx,yy);
else update(mid+1,rr,rson(kk),xx,yy);
pushup(kk);
}
int query(int ll,int rr,int kk,int findl,int findr){
if(ll>=findl&&rr<=findr) return tr[kk].minl;
int mid=(ll+rr)>>1;
int cal=maxw;
if(findl<=mid) cal=min(cal,query(ll,mid,lson(kk),findl,findr));
if(findr>mid) cal=min(cal,query(mid+1,rr,rson(kk),findl,findr));
return cal;
}
void update2(int ll,int rr,int kk,int xx,int yy){
if(ll==rr){
tr[kk].minl2=min(tr[kk].minl2,yy);
return ;
}
int mid=(ll+rr)>>1;
if(xx<=mid) update2(ll,mid,lson(kk),xx,yy);
else update2(mid+1,rr,rson(kk),xx,yy);
pushup(kk);
}
int query2(int ll,int rr,int kk,int findl,int findr){
if(ll>=findl&&rr<=findr) return tr[kk].minl2;
int mid=(ll+rr)>>1;
int cal=maxw;
if(findl<=mid) cal=min(cal,query2(ll,mid,lson(kk),findl,findr));
if(findr>mid) cal=min(cal,query2(mid+1,rr,rson(kk),findl,findr));
return cal;
}
signed main()
{
freopen("ut.in","r",stdin);
freopen("ut.out","w",stdout);
rd(n);
int tot=0;
for(int i=1;i<=n;i++){
rd(a[i].l), rd(a[i].r);
pos[++tot]=a[i].l; pos[++tot]=a[i].r;
}
sort(a+1,a+1+n);
sort(pos+1,pos+1+tot);
int len=unique(pos+1,pos+1+tot)-pos-1;
// cout << len <<endl;
build(1,len,1);
int ans=0;
for(int i=1;i<=n;i++){
int ll=lower_bound(pos+1,pos+1+len,a[i].l)-pos;
int rr=lower_bound(pos+1,pos+1+len,a[i].r)-pos;
// cout<<ll<<" "<<rr<<endl;
if(i>1){
int cal=max(a[i].l+a[i].r-query(1,len,1,ll,rr),a[i].r-a[i].l-(query2(1,len,1,ll,rr)));
ans=max(ans,cal);
}
update(1,len,1,rr,a[i].l+a[i].r);
update2(1,len,1,ll,a[i].r-a[i].l);
}
printf("%lld",ans);
return 0;
}
}
\(T\ 2\)
题意简述:
https://www.cnblogs.com/Krain428571/p/7426787.html
UPD:2023.10.4 鸽了