【学术篇】不知道该起啥标题了怎么办OvO

题目是2017.11.07的一次胡策的三道题目…
这次胡策呢, 骄傲的只打暴力打了个垫底…
solution出来之后呢, 又写了写.. 觉得应该纪念一下…

就这样吧= =

=====================我是可爱的分割线=========================

T1 还没肝出来我们先跳过…

=====================我是卖萌的分割线=========================

T2 B/b.cpp

题目描述:
有一条长为n的链, i号节点连接i+1号节点, 边的长度均为1.
给出m个点对, 你可以在任意两点间新建一条长为0的边, 要求建完以后使得m个点对中距离最长的尽量短. 输出这个最短距离. (n,m<=1e5) (n,m同级,以下复杂度均按n算好了…)

这题属于脑子不够开窍… 其实离正解挺近了…
然鹅并没有想到正解或者其他什么正确的思路, 打了个 O(n2) 暴力水了30pts…
暴力就不用讲了吧 枚举左右端点, 依次检查即可…

正解:
“距离最长的尽量短”, 很明显二分答案, 这也一眼能想到…
二分最长距离, 然后呢?

好吧, 其实 O(n2) 的check还是比较好想的… 所以其实能水60的, 但就是没有水60的欲望 (现在想想,那时的我真是聪明过分了…

考虑对于答案k, 能使每一对点间距离缩短为k的打洞(就这么叫了)地点是有规律的…
我们不妨暴力枚举一下..
对于 n=10,k=2, 使 dis(3,7)<=k 的点对共有:

(1,7),(2,6),(2,7),(2,8),(3,5),(3,6),(3,7),(3,8),(3,9),(4,6),(4,7),(4,8),(5,3)(5,7),(6,2),(6,3),(6,4),(7,1),(7,2),(7,3),(7,4),(7,5),(8,2),(8,3),(8,4),(9,3)

这么多, 我们把它们画在一个表格里的话, 就像这样:
这里写图片描述
发现这是两个旋转了45°的正方形…
而由于二分答案的check我们 只考虑可行与否,所以可以只看其中一个矩形, 我们以上面一个为例.
而斜正方形并不好处理,我们 逆时针转45°把它正过来..
容易看出, 这个转完得到的正方形的左上角是原来正方形最上面的点(即图中 (1,7) ), 这个点的坐标为 (lik,ri) (其中 li,ri 表示第i的点对的左右两个点)
右上角原来所在的则是最下面的点, 坐标为 (li+k,ri)

sin45° cos45° 都是 22 ,是个无理数, 但我们只考虑可行与否, 不妨都× 2 化为整数..
就有了下面的转化过程, 得到变换后的坐标:

(lik,ri)=>[(lik)cos45°risin45°,(lik)sin45°+ricos45°]=>(lirik,li+rik)(li+k,ri)=>[(li+k)cos45°risin45°,(li+k)sin45°+ricos45°]=>(liri+k,li+ri+k)

然后矩形求交显然可以线性维护边界…
就做完了
复杂度 O(nlogn)
代码(想清楚了就炒ez啦):

#include <cstdio>
const int INF=~0U>>1;
const int N=1e5;
int l[N],r[N],L,R,n,m;
inline int gn(){
    int a=0;char c=getchar();for(;c<'0'||c>'9';c=getchar());
    for(;c>47&&c<58;c=getchar())a=(a<<1)+(a<<3)+c-48;return a;
}
inline int max(const int &a,const int &b){if(a<b) return b;return a;} 
inline int min(const int &a,const int &b){if(a>b) return b;return a;} 
bool check(int k){
//(x-k,y) => ((x-k)cosd45-ysind45,(x-k)sind45+ycosd45) => (x-y-k,x+y-k)
//(x+k,y) => ((x+k)cosd45-ysind45,(x+k)sind45+ycosd45) => (x-y+k,x+y+k)
    int lx=-INF,ly=-INF,rx=INF,ry=INF;
    for(int i=1;i<=m;i++){
        if(r[i]-l[i]<=k) continue;
        lx=max(lx,l[i]-r[i]-k); ly=max(ly,l[i]+r[i]-k);
        rx=min(rx,l[i]-r[i]+k); ry=min(ry,l[i]+r[i]+k);
        if(lx>ly||rx>ry||lx>rx||ly>ry) return 0;
    }
    return 1;
}
int main(){ 
    freopen("b.in","r",stdin); freopen("b.out","w",stdout);
    n=gn(),m=gn(),L=1,R=n<<1; for(int i=1;i<=m;i++) l[i]=gn(),r[i]=gn();
    while(L<R){ int mid=(L+R)>>1; if(check(mid)) R=mid; else L=mid+1; }
    printf("%d",R);
}

唉 我好菜啊~~

====================我是机智的分割线======================

T3 哦 你来到了没有题解的荒原…………………

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值