@ivorysi
2018-01-02T08:48:01.000000Z
字数 30116
阅读 807
笔记
Have you ever played quoit in a playground? Quoit is a game in which flat rings are pitched at some toys, with all the toys encircled awarded.
In the field of Cyberground, the position of each toy is fixed, and the ring is carefully designed so it can only encircle one toy at a time. On the other hand, to make the game look more attractive, the ring is designed to have the largest radius. Given a configuration of the field, you are supposed to find the radius of such a ring.
Assume that all the toys are points on a plane. A point is encircled by the ring if the distance between the point and the center of the ring is strictly less than the radius of the ring. If two toys are placed at the same point, the radius of the ring is considered to be 0.
The input consists of several test cases. For each case, the first line contains an integer N (2 <= N <= 100,000), the total number of toys in the field. Then N lines follow, each contains a pair of (x, y) which are the coordinates of a toy. The input is terminated by N = 0.
For each test case, print in one line the radius of the ring required by the Cyberground manager, accurate up to 2 decimal places.
2
0 0
1 1
2
1 1
1 1
3
-1.5 0
0 0
0 1.5
0
0.71
0.00
0.75
这是一道平面分治
我们把平面分成两部分,每部分点数相等,然后我们考虑跨过中间那条线的距离
我们对于y坐标排序,边分治边归并排序,要保证这个坐标是是x-d,x+d,d是左右两边的点距离的最小值
然后每个点对于它对面的点进行匹配,要保证对面的点的y坐标要小于这个点的y,然后判断一下这两个y距离是不是大于d,如果大于就可以跳出
匹配的次数不会超过3次,因为对面的点和这面的某个点p距离小于d的不超过3个,否则d可以更小
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <cmath>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
//#define ivorysi
using namespace std;
struct Point {
double x,y;
Point() {}
Point(double _x,double _y) {
x=_x;y=_y;
}
bool operator < (const Point &rhs) const{
return x<rhs.x;
}
friend Point operator -(const Point &a,const Point &b) {
return Point(a.x-b.x,a.y-b.y);
}
double norm() {
return sqrt(x*x+y*y);
}
}p[100005];
Point a[100005],b[100005],c[100005];
int b_cnt=0,c_cnt=0;
double solve(int l,int r) {
if(l==r) return 1e18;
int mid=(l+r)>>1;
double x0=(p[mid].x+p[mid+1].x)/2.0;
double d=min(solve(l,mid),solve(mid+1,r));
b_cnt=0;
c_cnt=0;
int L=l,R=mid+1,tot=l;
while(L<=mid && R<=r) {
if(p[L].y<p[R].y) {
if(p[L].x>=(x0-d)) b[++b_cnt]=p[L];
a[tot++]=p[L++];
}
else {
if(p[R].x<=(x0+d)) c[++c_cnt]=p[R];
a[tot++]=p[R++];
}
}
while(L<=mid) {
if(p[L].x>(x0-d)) b[++b_cnt]=p[L];
a[tot++]=p[L++];
}
while(R<=r) {
if(p[R].x<(x0+d)) c[++c_cnt]=p[R];
a[tot++]=p[R++];
}
siji(i,l,r) p[i]=a[i];
int z=1,s=1;
while(z<=b_cnt || s<=c_cnt) {
if(z<=b_cnt && (s>c_cnt || b[z].y<c[s].y) ) {
gongzi(i,s-1,1) {
if(b[z].y-d>=c[i].y) break;
d=min(d,(c[i]-b[z]).norm());
}
++z;
}
else {
gongzi(i,z-1,1) {
if(c[s].y-d>=b[i].y) break;
d=min(d,(c[s]-b[i]).norm());
}
++s;
}
}
return d;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int n;
while(scanf("%d",&n)!=EOF) {
if(n==0) break;
siji(i,1,n) {
scanf("%lf%lf",&p[i].x,&p[i].y);
}
sort(p+1,p+n+1);
printf("%.2lf\n",solve(1,n)/2.0);
}
}
我们定义“区间的价值”为一段区间的最大值*最小值。
一个区间左端点在L,右端点在R,那么该区间的长度为(R−L+1)。
现在聪明的杰西想要知道,对于长度为k的区间,最大价值的区间价值是多少。
当然,由于这个问题过于简单。
我们肯定得加强一下。
我们想要知道的是,对于长度为1∼n的区间,最大价值的区间价值分别是多少。
长度为1的最优区间为2−2 答案为6∗6
长度为2的最优区间为4−5 答案为4∗4
长度为3的最优区间为2−4 答案为2∗6
长度为4的最优区间为2−5 答案为2∗6
长度为5的最优区间为1−5 答案为1∗6
多组测试数据
第一行一个数n(1≤n≤100000)。
第二行n个正整数(1≤ai≤109),下标从1开始。
由于某种不可抗力,ai的值将会是1∼109内随机产生的一个数。(除了样例)
输出共n行,第i行表示区间长度为i的区间中最大的区间价值。
5
1 6 2 4 4
36
16
12
12
6
这道题我们就分治,每次找到最小值,然后分成左右两个部分
找到当前每个长度的最大值,然后从短的往长的更新(因为我们强制要求了最小值就是我们找到的最小值)
然后更新一遍ans
因为数据随机所以不会被卡
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <cmath>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
//#define ivorysi
using namespace std;
typedef long long ll;
ll ans[MAXN],a[MAXN],tmp[MAXN];
void solve(int l,int r) {
if(r<l) {
return;
}
int p=l;
siji(i,l+1,r) {
if(a[i]<a[p]) p=i;
}
siji(i,1,r-l+1) tmp[i]=0;
gongzi(i,p,l) {
tmp[p-i+1]=max(tmp[p-i+1],a[i]*a[p]);
}
siji(i,p,r) {
tmp[i-p+1]=max(tmp[i-p+1],a[i]*a[p]);
}
siji(i,1,r-l+1) {
tmp[i]=max(tmp[i],tmp[i-1]);
ans[i]=max(ans[i],tmp[i]);
}
solve(l,p-1);
solve(p+1,r);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int n;
while(scanf("%d",&n)!=EOF) {
siji(i,1,n) {
scanf("%lld",&a[i]);
}
memset(ans,0,sizeof(ans));
solve(1,n);
siji(i,1,n) {
printf("%lld\n",ans[i]);
}
}
}
A set of points on a plane is called good, if for any two points at least one of the three conditions is true:
those two points lie on same horizontal line;
those two points lie on same vertical line;
the rectangle, with corners in these two points, contains inside or on its borders at least one point of the set, other than these two. We mean here a rectangle with sides parallel to coordinates' axes, the so-called bounding box of the two points.
You are given a set consisting of n points on a plane. Find any good superset of the given set whose size would not exceed 2·105 points.
The first line contains an integer n (1 ≤ n ≤ 104) — the number of points in the initial set. Next n lines describe the set's points. Each line contains two integers xi and yi ( - 109 ≤ xi, yi ≤ 109) — a corresponding point's coordinates. It is guaranteed that all the points are different.
Print on the first line the number of points m (n ≤ m ≤ 2·105) in a good superset, print on next m lines the points. The absolute value of the points' coordinates should not exceed 109. Note that you should not minimize m, it is enough to find any good superset of the given set, whose size does not exceed 2·105.
All points in the superset should have integer coordinates.
2
1 1
2 2
3
1 1
2 2
1 2
这道题的题面意思是找到一个更大的集合包含给出的集合,这个集合随便找但是大小不要超过2×10^5
使得任意两点之间满足下列三种条件之一
1.两点在同一条水平线
2.两点在同一条竖直线
3.两点构成的矩形中间包含一个点,可以在边界和里面(矩形的边和x轴y轴平行)
我们可以将点排序然后分成两份,让左右边每一个点向中间点所在的竖直线做一条垂线,这样做出的点肯定和剩下所有点都合法,然后递归的层数不会超过log n层,那么我们总共做出的点的个数不会超过n log n个
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
//#define ivorysi
using namespace std;
typedef long long ll;
int n;
struct Point {
int x,y;
Point() {}
Point(int _x,int _y) {
x=_x;y=_y;
}
bool operator < (const Point &rhs) const {
return x<rhs.x || (x==rhs.x && y<rhs.y);
}
bool operator == (const Point &rhs) const {
return x==rhs.x && y==rhs.y;
}
}p[10005];
set<Point> s;
void solve(int l,int r) {
if(r<=l) return;
int mid=(l+r)>>1;
siji(i,l,r) {
s.insert(Point(p[mid].x,p[i].y));
}
solve(l,mid-1);
solve(mid+1,r);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d",&n);
siji(i,1,n) {
scanf("%d%d",&p[i].x,&p[i].y);
s.insert(p[i]);
}
sort(p+1,p+n+1);
solve(1,n);
set<Point>::iterator iter;
printf("%d\n",s.size());
for(iter=s.begin();iter!=s.end();++iter) {
printf("%d %d\n",iter->x,iter->y);
}
return 0;
}
Xaviera现在遇到了一个有趣的问题。
平面上有N个点,Xaviera想找出周长最小的三角形。
由于点非常多,分布也非常乱,所以Xaviera想请你来解决这个问题。
为了减小问题的难度,这里的三角形也包括共线的三点。
第一行包含一个整数N表示点的个数。
接下来N行每行有两个整数,表示这个点的坐标。
输出只有一行,包含一个6位小数,为周长最短的三角形的周长(四舍五入)。
4
1 1
2 3
3 3
3 4
3.414214
100%的数据中N≤200000。
这是一道平面分治的题,我一开始认为只有两个相邻的点能做底边……
然后又开始枚举左边一个点,右边一个点,第三个点从右边点开始往上找,找到和右边的点距离大于d/2的就跳出
又gg了
因为有些时候,可能y坐标靠上的,和某个点距离反而小………………
然后我改对了我枚举的姿势
然后我的冒泡排序(对于三个点排序)写跪了,把i+1写成l+1了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define esp 1e-8
//#define ivorysi
using namespace std;
typedef long long ll;
int n;
struct Point {
double x,y;
Point() {}
Point(double _x,double _y) {
x=_x;y=_y;
}
bool operator < (const Point &rhs) const {
return x<rhs.x || (x==rhs.x && y<rhs.y);
}
bool operator == (const Point &rhs) const {
return x==rhs.x && y==rhs.y;
}
friend Point operator - (const Point &a,const Point &b) {
return Point(a.x-b.x,a.y-b.y);
}
double norm() {
return sqrt(x*x+y*y);
}
}p[400005];
Point a[400005],b[400005],c[400005];
int b_cnt,c_cnt;
double calc(Point l,Point z,Point s) {
return (l-z).norm()+(l-s).norm()+(z-s).norm();
}
double solve(int l,int r) {
if(r==l) {
return 1e20;
}
int mid=(l+r)>>1;
double x0=(p[mid].x+p[mid+1].x)/2.0;
double d=min(solve(l,mid),solve(mid+1,r));
b_cnt=c_cnt=0;
int L=l,R=mid+1,tot=l;
while(L<=mid && R<=r) {
if(p[L].y<p[R].y) {
if(p[L].x>=x0-d/2) b[++b_cnt]=p[L];
a[tot++]=p[L++];
}
else {
if(p[R].x<=x0+d/2) c[++c_cnt]=p[R];
a[tot++]=p[R++];
}
}
while(L<=mid) {
if(p[L].x>=x0-d/2) b[++b_cnt]=p[L];
a[tot++]=p[L++];
}
while(R<=r) {
if(p[R].x<=x0+d/2) c[++c_cnt]=p[R];
a[tot++]=p[R++];
}
siji(i,l,r) p[i]=a[i];
if(l+1==r) return 1e20;
int pos=1;
siji(i,1,b_cnt) {
while(pos<=c_cnt && b[i].y-c[pos].y >d/2) ++pos;
siji(j,pos,c_cnt) {
if(fabs(c[j].y-b[i].y) > d/2) break;
siji(k,j+1,c_cnt) {
if(fabs(c[k].y-b[i].y)>d/2) break;
d=min(d,calc(b[i],c[k],c[j]));
}
}
}
pos=1;
siji(i,1,c_cnt) {
while(pos<=b_cnt && c[i].y-b[pos].y >d/2) ++pos;
siji(j,pos,b_cnt) {
if(fabs(b[j].y-c[i].y) > d/2) break;
siji(k,j+1,b_cnt) {
if(fabs(b[k].y-c[i].y)>d/2) break;
d=min(d,calc(c[i],b[j],b[k]));
}
}
}
return d;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d",&n);
siji(i,1,n) {
scanf("%lf%lf",&p[i].x,&p[i].y);
}
sort(p+1,p+n+1);
printf("%.6lf\n",solve(1,n));
return 0;
}
Give a tree with n vertices,each edge has a length(positive integer less than 1001).
Define dist(u,v)=The min distance between node u and v.
Give an integer k,for every pair (u,v) of vertices is called valid if and only if dist(u,v) not exceed k.
Write a program that will count how many pairs which are valid for a given tree.
The input contains several test cases. The first line of each test case contains two integers n, k. (n<=10000) The following n-1 lines each contains three integers u,v,l, which means there is an edge between node u and v of length l.
The last test case is followed by two zeros.
For each test case output the answer on a single line.
5 4
1 2 3
1 3 1
1 4 2
3 5 1
0 0
8
这道题是树分治的板子题
我们把树按照重心划分,路径有三种情况,一是过重心,一是不过重心,一是以重心为端点
不过重心可以靠递归继续完成
而过重心和以重心为端点的,直接计算,d_u+d_v<=k,然后去掉所有u和v在一棵子树里的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define esp 1e-8
//#define ivorysi
using namespace std;
typedef long long ll;
int n,k;
struct node {
int val,to,next;
}edge[200005];
int head[20005],sumedge,fa[20005],size[20005],son[20005],dis[20005],d[20005];
ll ans;
bool vis[20005];
void add(int u,int v,int w) {
edge[++sumedge].to=v;
edge[sumedge].val=w;
edge[sumedge].next=head[u];
head[u]=sumedge;
}
void addtwo(int u,int v,int w) {
add(u,v,w);
add(v,u,w);
}
int que[20005],ql,qr;
int calcG(int st) {
fa[st]=0;qr=1;
que[ql=1]=st;
while(ql<=qr) {
int u=que[ql++];
size[u]=1;son[u]=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
que[++qr]=v;
fa[v]=u;
}
}
}
int res=0,mx=qr;
gongzi(i,qr,1) {
int u=que[i];
son[u]=max(son[u],qr-size[u]);
if(son[u]<mx) {
mx=son[u];
res=u;
}
size[fa[u]]+=size[u];
if(size[u] > son[fa[u]]) son[fa[u]]=size[u];
}
return res;
}
ll calc(int st,int di) {
dis[st]=di;
fa[st]=0;que[ql=1]=st;qr=1;
d[1]=di;
while(ql<=qr) {
int u=que[ql++];
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
que[++qr]=v;
dis[v]=dis[u]+edge[i].val;
d[qr]=dis[v];
fa[v]=u;
}
}
}
int l=1,r=qr;
sort(d+1,d+qr+1);
ll res=0;
while(l<r) {
if(d[l]+d[r]<=k) {res+=r-l; ++l;}
else --r;
}
return res;
}
void solve(int u) {
int G=calcG(u);
vis[G]=1;
ans+=calc(G,0);
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) ans-=calc(v,edge[i].val);
}
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) solve(v);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
while(scanf("%d%d",&n,&k)!=EOF) {
if(n+k==0) break;
ans=0;
sumedge=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
int u,v,w;
xiaosiji(i,1,n) {
scanf("%d%d%d",&u,&v,&w);
addtwo(u,v,w);
}
solve(1);
printf("%lld\n",ans);
}
return 0;
}
聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。
输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。
以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。
5
1 2 1
1 3 2
1 4 1
2 5 3
13/25
13组点对分别是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。
树分治的又一道板题
边的三种情况,然后去除掉两个点在同一子树的,之后递归处理
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <cmath>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define esp 1e-8
#define MAXN 20005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,k;
struct node {
int to,next;
ll val;
}edge[MAXN*2];
int head[MAXN],sumedge,fa[MAXN],size[MAXN],son[MAXN];
ll ans,dis[MAXN],cnt[10];
bool vis[20005];
void add(int u,int v,ll w) {
edge[++sumedge].to=v;
edge[sumedge].val=w;
edge[sumedge].next=head[u];
head[u]=sumedge;
}
void addtwo(int u,int v,ll w) {
add(u,v,w);
add(v,u,w);
}
int que[20005],ql,qr;
int calcG(int st) {
fa[st]=0;qr=1;
que[ql=1]=st;
while(ql<=qr) {
int u=que[ql++];
size[u]=1;son[u]=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
que[++qr]=v;
fa[v]=u;
}
}
}
int res=0,mx=qr;
gongzi(i,qr,1) {
int u=que[i];
son[u]=max(son[u],qr-size[u]);
if(son[u]<mx) {
mx=son[u];
res=u;
}
size[fa[u]]+=size[u];
if(size[u] > son[fa[u]]) son[fa[u]]=size[u];
}
return res;
}
ll calc(int st,int di) {
dis[st]=di;
fa[st]=0;que[ql=1]=st;qr=1;
memset(cnt,0,sizeof(cnt));
cnt[dis[st]%3]++;
while(ql<=qr) {
int u=que[ql++];
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
que[++qr]=v;
dis[v]=dis[u]+edge[i].val;
cnt[dis[v]%3]++;
fa[v]=u;
}
}
}
ll res=0;
siji(i,1,qr) {
res+=cnt[(3-(dis[que[i]]%3)) %3];
}
return res;
}
void solve(int u) {
int G=calcG(u);
vis[G]=1;
ans+=calc(G,0);
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) ans-=calc(v,edge[i].val);
}
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) solve(v);
}
}
ll gcd(ll a,ll b) {
return b==0 ? a : gcd(b,a%b);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
while(scanf("%d",&n)!=EOF) {
if(n+k==0) break;
ans=0;
sumedge=0;
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
int u,v,w;
xiaosiji(i,1,n) {
scanf("%d%d%d",&u,&v,&w);
addtwo(u,v,w);
}
solve(1);
ll down=(ll)n*n;
ll g=gcd(down,ans);
ans/=g;down/=g;
printf("%lld/%lld\n",ans,down);
}
return 0;
}
给出一棵树,求一条路径上边的边数在[L,U]之间,要求每条路径的平均值最大,求这个最大的平均值
N<=1000000
树分治的题总是很轻易的就非常恶心
这道题一看就是分数规划……然后怎么判断有没有解就mengbier
后来说是把每条边的边权都减去二分到的数值,然后求一条路径边数在[L,U]之间,权值大于0
这个树分治的思路很明显,合并两棵子树的时候可以用单调队列(然而我单调队列总是跪)
然而……这道题有人加了一个新的数据,必须要求我们对于每个重心的子树按深度从小到大合并
这样时间复杂度才是严格的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
#define esp 1e-8
#define pii pair<int,int>
#define fi first
#define se second
//#define ivorysi
using namespace std;
typedef long long ll;
int n,S,T;
int read() {
char c=getchar();
int res=0;
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') {
res=res*10+c-'0';
c=getchar();
}
return res;
}
struct node {
int to,next,val;
}edge[MAXN*2];
int sumedge,head[MAXN];
void add(int u,int v,int c) {
edge[++sumedge].to=v;
edge[sumedge].next=head[u];
edge[sumedge].val=c;
head[u]=sumedge;
}
void addtwo(int u,int v,int c) {
add(u,v,c);
add(v,u,c);
}
int Gr[MAXN],tot;
bool vis[MAXN];
int que[MAXN],ql,qr,fa[MAXN],son[MAXN],size[MAXN],DEP[MAXN];
double L[MAXN],Z[MAXN],dis[MAXN];
vector<pii> ver[MAXN];
int calcG(int st) {
que[ql=qr=1]=st;
fa[st]=0;
while(ql<=qr) {
int u=que[ql++];
size[u]=1;son[u]=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
fa[v]=u;
que[++qr]=v;
}
}
}
int res=0,mx=qr;
gongzi(i,qr,1) {
int u=que[i];
son[u]=max(son[u],qr-size[u]);
if(son[u]<mx) {
res=u;mx=son[u];
}
size[fa[u]]+=size[u];
son[fa[u]]=max(son[fa[u]],size[u]);
}
return res;
}
void pre_dfs(int u) {
Gr[++tot]=calcG(u);
vis[Gr[tot]]=1;
for(int i=head[Gr[tot]];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) pre_dfs(v);
}
}
int dep[MAXN],Ln,Zn;
void calc(int st,double di,double dec) {
que[ql=qr=1]=st;fa[st]=0;
dis[st]=di;dep[st]=1;
Zn=1;
while(ql<=qr) {
int u=que[ql++];
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
que[++qr]=v;
fa[v]=u;
dis[v]=dis[u]+(double)edge[i].val-dec;
dep[v]=dep[u]+1;
Zn=dep[v];
}
}
}
siji(i,1,qr) {
Z[dep[que[i]]]=max(Z[dep[que[i]]],dis[que[i]]);
}
}
int dfs_for_dep(int u,int fa) {
int res=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa) {
res=max(res,dfs_for_dep(v,u));
}
}
return res+1;
}
bool check(double mid) {
memset(vis,0,sizeof(vis));
siji(i,1,n) {
int G=Gr[i];
vis[G]=1;
int mxdp=0;
for(int k=head[G];k;k=edge[k].next) {
int v=edge[k].to;
if(!vis[v]) {
int maxdep=dfs_for_dep(v,G);
mxdp=max(maxdep,mxdp);
ver[maxdep].push_back(make_pair(v,edge[k].val));
}
}
siji(j,1,mxdp) {L[j]=-1e13;Z[j]=-1e13;}
Ln=0;
siji(h,1,mxdp) {
int siz=ver[h].size();
xiaosiji(p,0,siz) {
calc(ver[h][p].fi,(double)ver[h][p].se-mid,mid);
ql=1,qr=0;
int b=Ln;
siji(k,1,Zn) {
while(b>=0 && k+b>=S) {
while(ql<=qr && L[que[qr]]<=L[b]) --qr;
que[++qr]=b;--b;
}
while(ql<=qr && que[ql]+k>T) {++ql;}
if(ql<=qr) {
if(L[que[ql]]+Z[k] >=0.0) {
siji(j,1,Ln) L[j]=-1e13;
siji(j,1,Zn) Z[j]=-1e13;
siji(j,h,mxdp) ver[j].clear();
return true;
}
}
}
if(Ln<Zn) Ln=Zn;
siji(k,1,Zn) {
if(k>=S && k<=T && Z[k]>=0.0) {
siji(j,1,Ln) L[j]=-1e13;
siji(j,1,Zn) Z[j]=-1e13;
siji(j,h,mxdp) ver[j].clear();
return true;
}
if(L[k]<Z[k]) L[k]=Z[k];
}
siji(k,1,Zn) Z[k]=-1e13;
}
ver[h].clear();
}
}
return false;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
n=read();S=read();T=read();
int x,y,z;
xiaosiji(i,1,n) {
x=read();y=read();z=read();
addtwo(x,y,z);
}
pre_dfs(1);
double l=0,r=1000000.0;
int num=40;
while(num--) {
double mid=(l+r)/2.0;
if(check(mid)) l=mid;
else r=mid;
}
printf("%.3lf\n",l);
return 0;
}
After the success of 2nd anniversary (take a look at problem FTOUR for more details), this 3rd year, Travel Agent SPOJ goes on with another discount tour.
The tour will be held on ICPC island, a miraculous one on the Pacific Ocean. We list N places (indexed from 1 to N) where the visitors can have a trip. Each road connecting them has an interest value, and this value can be negative (if there is nothing interesting to view there). Simply, these N places along with the roads connecting them form a tree structure. We will choose two places as the departure and destination of the tour.
Since September is the festival season of local inhabitants, some places are extremely crowded (we call them crowded places). Therefore, the organizer of the excursion hopes the tour will visit at most K crowded places (too tiring to visit many of them) and of course, the total number of interesting value should be maximum.
Briefly, you are given a map of N places, an integer K, and M id numbers of crowded place. Please help us to find the optimal tour. Note that we can visit each place only once (or our customers easily feel bored), also the departure and destination places don't need to be different.
There is exactly one case. First one line, containing 3 integers N K M, with 1 <= N <= 200000, 0 <= K <= M, 0 <= M <= N.
Next M lines, each line includes an id number of a crowded place.
The last (N - 1) lines describe (N - 1) two-way roads connected N places, form a b i, with a, b is the id of 2 places, and i is its interest value (-10000 <= i <= 10000).
Only one number, the maximum total interest value we can obtain.
8 2 3
3
5
7
1 3 1
2 3 10
3 4 -2
4 5 -1
5 7 6
5 6 5
4 8 3
12
We choose 2 and 6 as the departure and destination place, so the tour will be 2 -> 3 -> 4 -> 5 -> 6, total interest value = 10 + (-2) + (-1) + 5 = 12
* Added some unofficial cases
这道题可以树分治
涉及到两棵树的合并可以用单调队列,我们先按每棵树路径上最多的拥堵结点个数分个类,从小到大处理已经合并的子树
注意重心可能也是一个拥堵节点
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 200005
#define esp 1e-8
#define pii pair<int,int>
#define fi first
#define se second
#define inf 2000000005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,k,m;
int read() {
char c=getchar();
int neg=1,res=0;
while(c<'0' || c>'9') {
if(c=='-') neg=-1;
c=getchar();
}
while(c>='0' && c<='9') {
res=res*10+c-'0';
c=getchar();
}
return res*neg;
}
struct node {
int to,next,val;
}edge[MAXN*4];
int sumedge,head[MAXN],size[MAXN]
,son[MAXN],fa[MAXN],L[MAXN],Z[MAXN],Ln,Zn,dis[MAXN],num[MAXN],ans,crowd[MAXN];
bool vis[MAXN];
vector<pii> ver[MAXN];
void add(int u,int v,int c) {
edge[++sumedge].to=v;
edge[sumedge].next=head[u];
edge[sumedge].val=c;
head[u]=sumedge;
}
void addtwo(int u,int v,int c) {
add(u,v,c);
add(v,u,c);
}
int que[MAXN],ql,qr;
int calcG(int st) {
fa[st]=0;que[ql=qr=1]=st;
while(ql<=qr) {
int u=que[ql++];
size[u]=1,son[u]=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
fa[v]=u;
que[++qr]=v;
}
}
}
int res=0,mx=qr;
gongzi(i,qr,1) {
int u=que[i];
son[u]=max(son[u],qr-size[u]);
if(son[u]<mx) {
res=u;mx=son[u];
}
size[fa[u]]+=size[u];
son[fa[u]]=max(size[u],son[fa[u]]);
}
return res;
}
void calc(int st,int di) {
que[ql=qr=1]=st;fa[st]=0;dis[st]=di;
num[st]=crowd[st];
while(ql<=qr) {
int u=que[ql++];
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa[u]) {
dis[v]=dis[u]+edge[i].val;
fa[v]=u;
num[v]=num[u]+crowd[v];
que[++qr]=v;
}
}
}
gongzi(i,qr,1) {
int u=que[i];
Zn=max(Zn,num[u]);
Z[num[u]]=max(Z[num[u]],dis[u]);
}
}
int getdep(int u,int fa) {
int res=0;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa) {
res=max(res,getdep(v,u));
}
}
return res+crowd[u];
}
void solve(int u) {
int G=calcG(u);
vis[G]=1;
int mxdp=0;
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) {
int maxdep=getdep(v,G);
mxdp=max(mxdp,maxdep);
ver[maxdep].push_back(make_pair(v,edge[i].val));
}
}
siji(i,0,mxdp) L[i]=Z[i]=-inf;
L[0]=0;
Ln=0;
siji(i,0,mxdp) {
int siz=ver[i].size();
xiaosiji(j,0,siz) {
Zn=0;
calc(ver[i][j].fi,ver[i][j].se);
int b=min(k,Ln);
ql=1;qr=0;
while(b>=0) {
while(ql<=qr && L[que[qr]]<=L[b]) {--qr;}
que[++qr]=b;--b;
}
siji(h,0,Zn) {
if(Z[h]==-inf) continue;
while(ql<=qr && h+que[ql]+crowd[G]>k) ++ql;
if(ql<=qr && L[que[ql]]!=-inf) {
ans=max(ans,L[que[ql]]+Z[h]);
}
if(ql>qr || h>k) break;
}
if(Ln<Zn) Ln=Zn;
siji(h,0,Zn) L[h]=max(L[h],Z[h]);
siji(h,0,Zn) Z[h]=-inf;
}
ver[i].clear();
}
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) solve(v);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
n=read();k=read();m=read();
int x,y,z;
siji(i,1,m) {
x=read();
crowd[x]=1;
}
xiaosiji(i,1,n) {
x=read();y=read();z=read();
addtwo(x,y,z);
}
solve(1);
printf("%d\n",ans);
return 0;
}
有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),又三个整数表示。现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb。显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。
第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性
包含N行,分别表示评级为0...N-1的每级花的数量。
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
3
1
3
0
1
0
1
0
0
1
1 <= N <= 100,000, 1 <= K <= 200,000
cdq分治
我们先按s排序,然后进行相同属性的花合成一个
然后分治的时候按照c归并排序,每次用树状数组的算法求在左下角的点有多少个
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 200005
#define esp 1e-8
//#define ivorysi
using namespace std;
typedef long long ll;
int n,k;
struct node {
int s,c,m;
int sum,cnt;
bool operator < (const node &rhs) const {
if(s!=rhs.s) return s<rhs.s;
if(c!=rhs.c) return c<rhs.c;
return m<rhs.m;
}
bool operator == (const node &rhs) const {
return s==rhs.s && c==rhs.c && m==rhs.m;
}
bool operator != (const node &rhs) const {
return !(*this==rhs);
}
}l[MAXN],z[MAXN];
int tr[MAXN],ans[MAXN],tot;
int lowbit(int x) {return x&(-x);}
void insert(int pos,int val) {
while(pos<=k) {
tr[pos]+=val;
pos+=lowbit(pos);
}
}
int query(int pos) {
int res=0;
while(pos>0) {
res+=tr[pos];
pos-=lowbit(pos);
}
return res;
}
void clear(int pos) {
while(pos<=k) {
if(tr[pos]!=0) tr[pos]=0;
else break;
pos+=lowbit(pos);
}
}
void Cdqsolve(int le,int ri) {
if(le==ri) return;
int mid=(le+ri)>>1;
Cdqsolve(le,mid);Cdqsolve(mid+1,ri);
int L=le,R=mid+1,id=le;
while(L<=mid && R<=ri) {
if(z[L].c<=z[R].c) {
insert(z[L].m,z[L].cnt);
l[id++]=z[L++];
}
else {
z[R].sum+=query(z[R].m);
l[id++]=z[R++];
}
}
while(L<=mid) {
insert(z[L].m,z[L].cnt);
l[id++]=z[L++];
}
while(R<=ri) {
z[R].sum+=query(z[R].m);
l[id++]=z[R++];
}
siji(i,le,ri) {
z[i]=l[i];clear(z[i].m);
}
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&k);
siji(i,1,n) {
scanf("%d%d%d",&l[i].s,&l[i].c,&l[i].m);
}
sort(l+1,l+n+1);
node last=l[1];
int id=1;
siji(i,2,n) {
if(l[i]!=last) {
z[++tot]=last;
z[tot].cnt=i-id;
last=l[i];
id=i;
}
}
z[++tot]=last;
z[tot].cnt=n+1-id;
Cdqsolve(1,tot);
siji(i,1,tot) {
ans[z[i].sum+z[i].cnt-1]+=z[i].cnt;
}
xiaosiji(i,0,n) {
printf("%d\n",ans[i]);
}
return 0;
}
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
对于每个输入2,输出一行,即输入2的答案
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
3
5
保证答案不会超过int范围
排序的时候忘记增加操作应该更靠前是真的尴尬…………
我们把查询操作拆成四个矩阵前缀和的加减,然后把这些点按x排序,按照树状数组插入二维平面上的点的算法给每一个询问加上对应的值
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 200005
//#define ivorysi
using namespace std;
typedef long long ll;
int s,w,tot;
ll ans[MAXN],tr[MAXN*10];
struct node {
int id,op,x,y,pos;
ll d;
bool operator < (const node &rhs) const {
if(x==rhs.x && y==rhs.y) return op<rhs.op;
if(x!=rhs.x) return x<rhs.x;
return y<rhs.y;
}
}qry[MAXN],tmp[MAXN];
int lowbit(int x) {return x&(-x);}
void insert(int pos,ll val) {
while(pos<=w) {
tr[pos]+=val;
pos+=lowbit(pos);
}
}
ll query(int pos) {
ll res=0;
while(pos>0) {
res+=tr[pos];
pos-=lowbit(pos);
}
return res;
}
void Cdqsolve(int l,int r) {
if(l==r) return;
int mid=(l+r)>>1;
siji(i,l,r) {
if(qry[i].id<=mid && qry[i].op==1) insert(qry[i].y,qry[i].d);
if(qry[i].id>mid && qry[i].op==2) ans[qry[i].pos]+=query(qry[i].y)*qry[i].d;
}
siji(i,l,r) {
if(qry[i].id<=mid && qry[i].op==1) insert(qry[i].y,-qry[i].d);
}
int idx1=l,idx2=mid+1;
siji(i,l,r) {
if(qry[i].id<=mid) tmp[idx1++]=qry[i];
else tmp[idx2++]=qry[i];
}
siji(i,l,r) {
qry[i]=tmp[i];
}
Cdqsolve(l,mid);Cdqsolve(mid+1,r);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&s,&w);
int cnt=0,op,x1,y1,a,x2,y2;
while(scanf("%d",&op)!=EOF) {
if(op==3) break;
if(op==1) {
scanf("%d%d%d",&x1,&y1,&a);
qry[++cnt]=(node){cnt,op,x1,y1,0,a};
}
else {
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
qry[++cnt]=(node){cnt,op,x2,y2,++tot,1};
qry[++cnt]=(node){cnt,op,x2,y1-1,tot,-1};
qry[++cnt]=(node){cnt,op,x1-1,y2,tot,-1};
qry[++cnt]=(node){cnt,op,x1-1,y1-1,tot,1};
ans[tot]=(ll)(x2-x1+1)*(y2-y1+1)*s;
}
}
sort(qry+1,qry+cnt+1);
Cdqsolve(1,cnt);
siji(i,1,tot) {
printf("%lld\n",ans[i]);
}
return 0;
}
对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
输出包含m行,依次为删除每个元素之前,逆序对的个数。
5 4
1
5
3
4
2
5
1
4
2
5
2
2
1
如何转化成对于单个元素求贡献,就是转化一下求删掉这个元素能少几个逆序对
从前和从后分别跑一次树状数组求逆序对就好了
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 100005
#define esp 1e-8
#define pii pair<int,int>
#define fi first
#define se second
#define inf 2000000005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,m;
struct node {
int num,pos,ti;
bool operator < (const node &rhs) const {
return pos<rhs.pos;
}
}qry[MAXN],tmp[MAXN];
int a[MAXN],pos[MAXN];
ll init,tr[MAXN],ans[MAXN];
bool used[MAXN];
int lowbit(int x) {return x&(-x);}
void insert(int pos,ll val) {
while(pos<=n) {
tr[pos]+=val;
pos+=lowbit(pos);
}
}
ll query(int pos) {
ll res=0;
while(pos>0) {
res+=tr[pos];
pos-=lowbit(pos);
}
return res;
}
void Cdqsolve(int l,int r) {
if(l==r) return;
int mid=(l+r)>>1;
siji(i,l,r) {
if(qry[i].ti>mid) insert(qry[i].num,1);
else ans[qry[i].ti]+=query(n)-query(qry[i].num-1);
}
siji(i,l,r) {
if(qry[i].ti>mid) insert(qry[i].num,-1);
}
gongzi(i,r,l) {
if(qry[i].ti>mid) insert(qry[i].num,1);
else ans[qry[i].ti]+=query(qry[i].num);
}
siji(i,l,r) {
if(qry[i].ti>mid) insert(qry[i].num,-1);
}
int idx1=l,idx2=mid+1;
siji(i,l,r) {
if(qry[i].ti<=mid) tmp[idx1++]=qry[i];
else tmp[idx2++]=qry[i];
}
siji(i,l,r) {
qry[i]=tmp[i];
}
Cdqsolve(l,mid);Cdqsolve(mid+1,r);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
siji(i,1,n) {
scanf("%d",&a[i]);
pos[a[i]]=i;
}
int x,cnt=m;
siji(i,1,m) {
scanf("%d",&x);
qry[i].num=x;
qry[i].pos=pos[x];
qry[i].ti=i;
used[x]=1;
}
siji(i,1,n) {
if(!used[i]) {
qry[++cnt].num=i;
qry[cnt].pos=pos[i];
qry[cnt].ti=cnt;
}
}
gongzi(i,n,1) {
init+=query(a[i]);
insert(a[i],1);
}
memset(tr,0,sizeof(tr));
sort(qry+1,qry+n+1);
Cdqsolve(1,n);
siji(i,1,m) {
printf("%lld\n",init);
init-=ans[i];
}
}
给出n个点
m次操作
1 x y 表示再插入一个点x y
2 x y 表示询问离x y最近的点的距离
我们可以先拆绝对值的式子,这样我们强制让每个点在这个点左下角,剩下的三种情况可以通过翻转获得
我们记录插入点x+y根据y来存储的最大值,每次询问去查询y值小于询问点y值的最大值
套用cdq分治的板子
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
#define MAXN 500005
//#define ivorysi
using namespace std;
typedef long long ll;
int n,m;
struct node {
int id,op,x,y,pos;
bool operator < (const node &rhs) const{
if(x==rhs.x && y==rhs.y) return op<rhs.op;
if(x!=rhs.x) return x<rhs.x;
return y<rhs.y;
}
}qry[MAXN*2],tmp[MAXN*2];
int ans[MAXN*2],tot;
int maxsize=1000005;
int tr[2000010];
int lowbit(int x) {return x&(-x);}
void insert(int pos,int val) {
while(pos<=maxsize) {
tr[pos]=max(tr[pos],val);
pos+=lowbit(pos);
}
}
int query(int pos) {
int res=0;
while(pos>0) {
res=max(res,tr[pos]);
pos-=lowbit(pos);
}
return res;
}
void clear(int pos) {
while(pos<=maxsize) {
tr[pos]=0;
pos+=lowbit(pos);
}
}
void Cdqsolve(int l,int r) {
if(l==r) return;
int mid=(l+r)>>1;
siji(i,l,r) {
if(qry[i].id<=mid && qry[i].op==1) {
insert(qry[i].y,qry[i].y+qry[i].x);
}
if(qry[i].id>mid && qry[i].op==2) {
int t=query(qry[i].y);
if(t!=0) {
if(qry[i].x+qry[i].y-t<0) {
printf("--------\n");
printf("%d\n",t);
printf("%d %d %d\n",i,qry[i].x,qry[i].y);
printf("--------\n");
}
ans[qry[i].pos]=min(ans[qry[i].pos],qry[i].x+qry[i].y-t);
}
}
}
int idx1=l,idx2=mid+1;
siji(i,l,r) {
if(qry[i].id<=mid && qry[i].op==1) {
clear(qry[i].y);
}
if(qry[i].id<=mid) tmp[idx1++]=qry[i];
else tmp[idx2++]=qry[i];
}
siji(i,l,r) qry[i]=tmp[i];
Cdqsolve(l,mid);
Cdqsolve(mid+1,r);
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
int t,x,y;
siji(i,1,n) {
scanf("%d%d",&x,&y);
++x;++y;
qry[i]=(node){i,1,x,y,0};
}
siji(i,1,m) {
scanf("%d%d%d",&t,&x,&y);
++x;++y;
if(t==1) {
qry[i+n]=(node){i+n,1,x,y,0};
}
else {
qry[i+n]=(node){i+n,2,x,y,++tot};
ans[tot]=0x7fffffff;
}
}
sort(qry+1,qry+n+m+1);
Cdqsolve(1,n+m);
siji(i,1,n+m) {
qry[i].y=maxsize-qry[i].y;
}
sort(qry+1,qry+n+m+1);
Cdqsolve(1,n+m);
siji(i,1,n+m) {
qry[i].x=maxsize-qry[i].x;
}
sort(qry+1,qry+n+m+1);
Cdqsolve(1,n+m);
siji(i,1,n+m) {
qry[i].y=maxsize-qry[i].y;
}
sort(qry+1,qry+n+m+1);
Cdqsolve(1,n+m);
siji(i,1,tot) {
printf("%d\n",ans[i]);
}
return 0;
}