17年的合宿好难啊。。。感觉是我做过的最难的一套题(没有之一)了。。。但是可能也是价值最高的?
Day1:
T1 Cultivation:给你一个H*W的网格,有N<=300棵仙人掌。每一年可以选择一个方向(例如向上),使得每一棵仙人掌的上面都长出一棵仙人掌(如果原来就有就不变),求最少的操作次数使得每个格子都有一棵仙人掌。
考虑将上下和左右分开来考虑。对于一维的情况,求出A,B,C,分别表示最左边的点向左的距离,相邻两个点的最大距离,最右边的点向右的距离,则答案为max(A+C,B)。
现在考虑将向左移动和向右移动都变为向右移动,然后再将H*W的网格整体向右移动。考虑枚举向右移动的长度,注意到N棵仙人掌将所有的列分成了2N段,对于每一段都求出独立的A,B,C,显然答案为当前H*W的网格包含的所有段的max(max{A}+max{C},max{B})。用单调队列实现即可。
另外实际上只需要考虑O(N^2)个向右移动的长度。有三种情况:
1.最小的将整个网络填满的长度
2.对于两棵满足横坐标x
3.对于任意两棵仙人掌,y-x+W-1。
复杂度O(N^3)。需要注意常数。
AC代码如下:
#include
#define ll long long
#define N 609
#define M 200009
using namespace std;
int H,W,n,m,cnt,len[M],p[N],q[N],A[N],B[N],C[N],ord[N];
bool bo[N][N]; ll ans=1ll<<60;
struct node{ ll x; int y; }a[N],b[N],c[N];
bool cmpx(node u,node v){ return u.x=a[c[q[tail]].y]; tail--);
q[++tail]=x;
}
int top(){ return c[q[head]].y; }
}f,g,h;
void add(int x,int y){
bo[x][y]=1;
int i,last=-1;
for (i=1; i<=n; i++) if (bo[x][y=ord[i]]){
if (last==-1){ A[x]=a[y].y-1; B[x]=0; }
else B[x]=max(B[x],a[y].y-last-1);
last=a[y].y;
}
C[x]=W-last;
}
int solve(int lim){
int i,j,k,ans=W<<1;
for (i=k=1,j=n+1; k<=m; k++)
c[k]=(i<=n && b[i].x<=b[j].x || j>m?b[i++]:b[j++]);
f.clr(); g.clr(); h.clr();
for (i=j=1; i<=m && c[i].x+H<=c[m].x; i++){
f.pop(i); g.pop(i); h.pop(i);
for (; j<=m && c[i].x+H>c[j].x; j++){
f.ins(j,A); g.ins(j,B); h.ins(j,C);
}
ans=min(ans,max(A[f.top()]+C[h.top()],B[g.top()]));
}
return ans;
}
int main(){
scanf("%d%d%d",&H,&W,&n); m=n<<1;
int i,j;
for (i=1; i<=n; i++) scanf("%lld%d",&a[i].x,&a[i].y);
sort(a+1,a+n+1,cmpx);
len[cnt=1]=a[1].x-1+H-a[n].x;
for (i=1; i=len[1])
len[++cnt]=a[i].x+H-a[j].x-1;
if (ilen[1])
len[++cnt]=a[j].x-a[i].x-1;
}
sort(len+1,len+cnt+1);
for (i=1; i<=n; i++) ord[i]=i;
sort(ord+1,ord+n+1,cmpy);
for (i=1; i<=n; i++) p[i]=q[i]=1;
for (i=1; i<=cnt; i++) if (i==1 || len[i]>len[i-1]){
for (j=1; j<=n; j++){
for (; p[j]<=n && a[j].x+len[i]>=a[p[j]].x; p[j]++)
if (a[j].x<=a[p[j]].x) add(p[j],j);
for (; q[j]<=n && a[j].x+len[i]+1>=a[q[j]].x; q[j]++)
if (a[j].x
T2 Port Facility:给定n个货物的进栈和出栈时间和2个栈,问有多少种装货的方式。
考虑给所有不能同时在同一个栈内的物体连边,然后先二分图染色判断合法,答案就是2^连通块个数。
连边方法有很多种。我的方法是按左端点排序后用set维护当前存在的货物的右端点,然后加入一个新的货物时在set内查询和它相交的点,只要所有的点和左右两个点连0边,然后最左边的点和它连1边。如果一个点和左右两边的点都相连就从set中删去。复杂度O(NlogN)。
AC代码如下:
#include
#define inf 1000000000
#define N 2000009
using namespace std;
int n,tot,tp,q[N],a[N],b[N],fst[N],pnt[N<<3],len[N<<3],nxt[N<<3];
int lf[N],rg[N],vis[N];
set S,T; set:: iterator it;
void add(int x,int y,int z){
pnt[++tot]=y; len[tot]=z; nxt[tot]=fst[x]; fst[x]=tot;
}
void ins(int x,int y,int z){
x=b[x]; y=b[y];
add(x,y,z); add(y,x,z);
}
void dfs(int x,int t){
if (vis[x]){
if (vis[x]!=t){ puts("0"); exit(0); }
return;
}
vis[x]=t;
int i;
for (i=fst[x]; i; i=nxt[i]) dfs(pnt[i],t^len[i]);
}
int main(){
scanf("%d",&n);
int i,k,x,y;
for (i=1; i<=n; i++){
scanf("%d%d",&x,&y);
a[x]=y; b[y]=i;
}
S.insert(-inf); S.insert(inf);
T.insert(-inf); T.insert(inf);
for (x=1; x<=(n<<1); x++) if (y=a[x]){
i=b[y];
it=S.upper_bound(y);
rg[y]=*it; it--; lf[y]=*it;
if (*it>x) ins(y,*it,3);
it=T.upper_bound(y); it--;
for (; *it>x; it--){
k=*it;
if (lf[k]>x){
ins(k,lf[k],0); lf[k]=-inf;
}
if (rg[k]
T3 Sparklers:有n个手中拿着烟花人站成一排,第k个人手上有一束燃着的烟花,烟花Ts后熄灭,一个人需要在Ts(含Ts)内将它传递给下一个人。已知人的奔跑速度v,求T的最小值使得所有人手中的烟花都能燃烧。
首先二分答案。考虑如何check。考虑到如果某一次结束后i-j的所有人手中的烟花都燃烧过了,那么这时手中烟花正在燃烧的人可能在的位置是[a[j]-T*(j-i),a[i]+T*(j-i)],也就是如果a[j]-a[i]<=2*T*(j-i),那么区间(i,j)满足条件。因此就是问能否从(k,k)走到(1,n)使得所有经过的区间都满足条件。
令b[i]=a[i]-i*T,(i,j)满足条件转化为b[i]>=b[j]。考虑当前正在(x,y),如果存在i满足min{b[i~x]}>=b[y]且b[i]>b[x]那么显然可以贪心走到(i,y)。y亦然。当不能走的时候,如果依然存在b[i](i
AC代码如下:
#include
#define ll long long
#define inf 1000000000000000000ll
#define N 100009
using namespace std;
int n,m,sta,tp,q[N],a0[N],lg2[N],lf[N],rg[N]; ll a[N];
struct node{ ll x; int y; }f[17][N],g[17][N];
bool operator <(node u,node v){ return u.xy) return (node){inf,x};
int k=lg2[y-x+1];
return min(f[k][x],f[k][y-(1<y) return (node){-inf,x};
int k=lg2[y-x+1];
return max(g[k][x],g[k][y-(1<=a[i]; tp--);
rg[i]=q[tp]; q[++tp]=i;
}
for (x=y=sta; x>1 || y=a[y]) x=u;
else if (v && getmax(y+1,v-1).x<=a[x]) y=v; else{
if (x==1) return getmax(y+1,n).x<=a[x];
if (y==n) return getmin(1,x-1).x>=a[y];
u=getmin(1,x-1).y; v=getmax(y+1,n).y;
if (a[u]a[x]) return 0;
i=getmax(1,u).y; j=getmin(v,n).y;
if (a[i]>=a[v]) x=i;
else if (a[j]<=a[u]) y=j; else return 0;
}
}
return 1;
}
int main(){
scanf("%d%d%d",&n,&sta,&m);
int i;
for (i=1; i<=n; i++) scanf("%d",&a0[i]);
for (i=2; i<=n; i++) lg2[i]=lg2[i>>1]+1;
int l=0,r=(a0[n]-a0[1])/m/2+1,mid;
//cerr<>1;
if (check(2ll*mid*m)) r=mid; else l=mid+1;
}
printf("%d\n",l);
return 0;
}
Day2:
T1 Arranging Tickets:给n个站排成一个环,i和i+1(n和1)连边,m种乘客,(A,B,C)表示从A到B,有C个这样的乘客。你可以给每一个乘客选择两种路径之一,使得经过最多的边的经过数量最少。
个人认为是本次joisc最难的一道题(如果考虑证明难度)。。。也是我唯一一道完全看题解的题。。。然而强如myy似乎还是轻松切了。看来我还是太菜了。
三句话概括:一波操作,两个引理,三个结论。。。
首先破环成链,二分答案ans。将每个乘客看成一条线段。然后考虑所有需要翻转的线段,它们的交集非空(否则考虑[a,b),[c,d),a
剩下的只需要考虑从左到右贪心然后用数据结构维护即可。
AC代码如下:
#include
#define ll long long
#define N 200009
using namespace std;
int n,m,fst[N],nxt[N],a[N],b[N],c[N],rst[N]; ll tg[N],icr[N];
struct cmp{
bool operator ()(int x,int y){ return b[x],cmp> Q;
bool check(int k,ll lim,ll num){
if (num>lim) return 0;
int i,j,x; ll tmp;
memset(fst,0,sizeof(fst));
for (i=1; i<=m; i++) if (a[i]<=k && b[i]>k){
nxt[i]=fst[a[i]]; fst[a[i]]=i; rst[i]=c[i];
}
while (!Q.empty()) Q.pop();
memset(icr,0,sizeof(icr));
for (i=1; i<=k; i++){
for (j=fst[i]; j; j=nxt[j]) Q.push(j);
tmp=max(0ll,tg[i]+num-lim)+1>>1;
while (tmp && !Q.empty()){
x=Q.top(); Q.pop();
if (rst[x]<=tmp){
tmp-=rst[x]; num-=rst[x]<<1;
icr[a[x]]-=rst[x]; icr[b[x]]+=rst[x]<<1;
} else{
rst[x]-=tmp; num-=tmp<<1; Q.push(x);
icr[a[x]]-=tmp; icr[b[x]]+=tmp<<1; tmp=0;
}
}
if (tmp) return 0;
}
for (i=1; i<=n; i++){
icr[i]+=icr[i-1];
if (icr[i]+tg[i]>lim) return 0;
}
return 1;
}
int main(){
scanf("%d%d",&n,&m);
int i,j,k;
for (i=1; i<=m; i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
for (i=1; i<=m; i++){
if (a[i]>b[i]) swap(a[i],b[i]);
tg[a[i]]+=c[i]; tg[b[i]]-=c[i];
}
for (i=2,j=k=1; itg[j]) j=i; if (tg[i]==tg[j]) k=i;
}
ll l=0,r=tg[j],mid;
while (l>1;
if (check(j,mid,tg[j]-mid) || check(j,mid,tg[j]-mid+1)
|| check(k,mid,tg[k]-mid) || check(k,mid,tg[k]-mid+1))
r=mid; else l=mid+1;
}
printf("%lld\n",l);
}
T2 Broken Device:通信题。压缩程序需要发送一个60位二进制数,其中有150位的编码空间,指定的40位只能为0。解码程序只知道这150位(不知道指定了哪40位)。
3位一段。对于某三位,如果没有坏点,我们让它传递至少2位信息;如果只有1个坏点,我们让它传递至少1位信息。简单构造即可。
AC代码如下:
#include
#include"Broken_device_lib.h"
#define ll long long
#define N 159
#define ad(x) a[len++]=x;
using namespace std;
int len,a[N],b[N],s[N];
void Anna(int n,ll x,int m,int p[]){
int i,j;
for (i=0; i<60; i++,x>>=1) a[i]=x&1;
memset(b,0,sizeof(b));
for (i=0; i=0; i--) s[i]=s[i+1]+b[i];
for (i=j=0; j<60 && i=60){
for (i=59; i>=0; i--) ans=ans<<1|a[i]; return ans;
}
}
}
啊这不是集训队作业(的加强版)吗。。。然后发现我做那道作业的办法并不能简单用来做这道题。
考虑倍增f[i][j],g[i][j]表示i走2^j步能到达的最左/最右的点的位置。然后询问先贪心走x,然后贪心走y即可。
AC代码如下:
#include
#define N 100009
using namespace std;
int n,m,a[N],q[N],f[N][17],g[N][17];
int main(){
scanf("%d%*d%d",&n,&m);
int i,j,l,r,x,y,u,v,ans;
for (i=1; i<=n; i++) scanf("%d",&a[i]);
q[j=1]=1;
for (i=2; i<=n; i++){
for (; j && a[i]>a[q[j]]; j--);
f[i][0]=q[j]; q[++j]=i;
}
q[j=1]=n;
for (i=n-1; i; i--){
for (; j && a[i]>a[q[j]]; j--);
g[i][0]=q[j]; q[++j]=i;
}
f[1][0]=1; g[n][0]=n;
for (j=1; j<=16; j++)
for (i=1; i<=n; i++){
f[i][j]=min(f[f[i][j-1]][j-1],f[g[i][j-1]][j-1]);
g[i][j]=max(g[f[i][j-1]][j-1],g[g[i][j-1]][j-1]);
}
while (m--){
scanf("%d%d",&x,&y); if (x>y) swap(x,y);
l=r=x; ans=0;
for (i=16; i>=0; i--){
u=min(f[l][i],f[r][i]); v=max(g[l][i],g[r][i]);
if (v=0; i--){
u=min(f[l][i],f[r][i]); v=max(g[l][i],g[r][i]);
if (u>x){ l=u; r=v; ans+=1<
Day3:
T1 Long Distance Coach:有一辆车从0行驶到X,其中有M个补给站a1~an。给定T,有N位乘客需要在Di+kT(k为整数)的时间喝一个单位水。司机需要在kT的时间喝水。Di互不相同。当车位于起点或者补给站时可以补充车上的水。如果某个时刻乘客想要喝水但是车上没有了,那么他会下车,你需要付出Ci的代价。补充一个单位水的代价为W。求最小的总代价(司机不能下车,乘客想喝水时不会位于补给站)。
考虑倒着dp。(选择一个人表示让他走到终点;不选择即中途下车)令f[i]表示选择了第i个人的最小总代价,g[i]表示不选择第i个人的总代价。把每个长度为T的时间看成一段,第i段中第j和j+1个人之间有个补给站,那么考虑两个人x<=j
这样就可以dp了。显然f[i]=max(f[i+1],g[i+1])+选择i的代价,然后g的转移方程可以看成是若干条直线在i点的最小值,维护一下这个凸壳即可。
AC代码如下:
#include
#define ll long long
#define N 200009
using namespace std;
int n,m,w,tp; ll L,T,c[N],p[N],b[N],f[N],g[N],s[N];
struct node{ ll x,y; }a[N],q[N];
bool cmpT(ll x,ll y){ return x%T1 && sig(q[tp].y-q[tp-1].y,q[tp].x-q[tp-1].x,i,1)>=0; tp--);
f[i]=min(f[i+1],g[i+1])+b[i];
g[i]=q[tp].y-q[tp].x*i-s[i-1];
u=(node){c[i],min(f[i]+c[i]*i+s[i-1],q[tp].y-q[tp].x*i+c[i]*i)};
for (; tp && c[i]<=q[tp].x; tp--);
for (; tp>1 && sig(u.y-q[tp].y,u.x-q[tp].x,q[tp].y-q[tp-1].y,q[tp].x-q[tp-1].x)<=0; tp--);
q[++tp]=u;
}
printf("%lld\n",min(f[1],g[1])+(L/T+1)*w);
return 0;
}
T2 Long Mansion:有n个房间,每个房间有若干钥匙。i个i+1之间的门需要特定的钥匙。多次询问x,y表示能否从x走到y。
用[Li,Ri]表示从i出发最远能走到的位置。预处理f[i],g[i]表示i左侧第一个能打开i号门的房间,g[i]表示右侧。考虑从左到右求。若Ri-1>=i,显然i最远只能走到Ri。如果i能走到i-1显然[Li,Ri]=[Li-1,Ri-1],否则只能向右走,二分即可。否则只需要贪心每次向两边走即可。因为没走两次右端点必然拓展1,复杂度O(NlogN)。
AC代码如下:
#include
#define N 500009
using namespace std;
int n,m,tot,fst[N],pnt[N],nxt[N],a[N],last[N],lg2[N],f[19][N],g[19][N],lf[N],rg[N];
void add(int x,int y){
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
int getmin(int x,int y){
if (x>y) return n+1;
int k=lg2[y-x+1];
return min(f[k][x],f[k][y-(1<y) return 0;
int k=lg2[y-x+1];
return max(g[k][x],g[k][y-(1<>1]+1;
for (i=1; i<=18; i++)
for (j=1; j=i){
l=i; r=rg[i-1];
while (l>1;
if (getmin(i,mid-1)>=i) l=mid; else r=mid-1;
}
rg[i]=l;
if (g[0][i-1]<=l){
lf[i]=lf[i-1]; rg[i]=rg[i-1];
} else lf[i]=i;
} else{
l=1; r=i;
while (l>1;
if (getmax(mid,i-1)<=i) r=mid; else l=mid+1;
}
lf[i]=l; rg[i]=i;
while (1){
l=rg[i]; r=n;
while (l>1;
if (getmin(i,mid-1)>=lf[i]) l=mid; else r=mid-1;
}
if (l==rg[i]) break; else rg[i]=l;
l=1; r=lf[i];
while (l>1;
if (getmax(mid,i-1)<=rg[i]) r=mid; else l=mid+1;
}
if (l==lf[i]) break; else lf[i]=l;
}
}
scanf("%d",&m);
while (m--){
scanf("%d%d",&x,&y);
puts(y>=lf[x] && y<=rg[x]?"YES":"NO");
}
return 0;
}
首先考虑一条链的情况。现在随意选择一个不再链上的点x,首先判断和链的那一侧较近(记为y)。然后每次logn二分查找位于x~y上最小的点z然后递归(x,z)(y,z),找不到则存在边x->y。
如果是一棵树那么先将y定为树根,然后先用上述过程找到一个直接连在树上的点x,然后按照bfs序二分查找x和那个点相连。
如果是图的话,在上述过程之后将x和当前的图中相连的那个点删去,这样最多变成7个连通图。对每个连通图先判断有没有和x相连的边然后二分。复杂度O(7N+NlogN+NlogN)。
AC代码如下:
#include
#include"park.h"
#define N 1509
using namespace std;
int n,tot,fst[N],pnt[N<<1],nxt[N<<1],p[N],h[N],d[N],vis[N]; bool bo[N];
void add(int x,int y){
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
bool check(int x){
int i;
for (i=1; i<=n; i++) p[i]=(vis[i]==1 || i==x);
return Ask(0,x-1,p+1);
}
int calc(int x){
int i,l=1,r=n,mid;
while (l>1;
memset(p,0,sizeof(p));
for (i=1; i<=mid; i++) p[i]=(vis[i]!=2);
for (i=mid+1; i<=n; i++) p[i]=(vis[i]==1);
p[x]=1;
if (Ask(0,x-1,p+1)) r=mid; else l=mid+1;
}
return l;
}
int bfs(int sta){
int head=0,tail=1,i,x,y; h[1]=sta;
memset(d,-1,sizeof(d)); d[sta]=1;
while (head>1;
memset(p,0,sizeof(p)); p[x]=1;
for (j=1; j<=mid; j++) p[h[j]]=1;
if (Ask(min(i,x)-1,max(i,x)-1,p+1)) r=mid; else l=mid+1;
}
l=h[l];
bo[l]=0; Answer(min(x,l)-1,max(x,l)-1);
add(x,l); add(l,x);
} else
while (cnt) bo[h[cnt--]]=0;
}
vis[x]=1;
}
void Detect(int T,int n0){
n=n0; vis[1]=1;
int i;
for (i=2; i<=n; i++) if (!vis[i]) solve(i);
}
给N+M条街道,每条街道都有一个重要度。Q次询问,每次有一个人从(x,y)出发,遇到一条比当前重要度高的街道就拐弯。问最长经过多少时间走出整个地图。
拐弯的时候大力枚举两个方向。这样对于单次询问总的状态数是O(N)的。对于Q次询问,有高超的技巧证明总的复杂度不是O(QN)而是O(Q^0.5*N)。因此开个hash或者map记录即可。
AC代码如下:
#include
#define ll long long
#define up(x,y) (x<(y)?x=(y):0)
#define N 50009
#define M 10000003
using namespace std;
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
int m,n,cas,a[N],b[N],f[4][16][N];
struct hsh{
int tot,fst[20000003],px[M],py[M],pz[M],nxt[M]; ll len[M];
ll qry(int x,int y,int z){
int k=(x*12233ll+y*666ll+z)%20000003,i;
for (i=fst[k]; i; i=nxt[i])
if (px[i]==x && py[i]==y && pz[i]==z) return len[i];
return 0;
}
void ins(int x,int y,int z,ll t){
int k=(x*12233ll+y*666ll+z)%20000003;
px[++tot]=x; py[tot]=y; pz[tot]=z; len[tot]=t;
nxt[tot]=fst[k]; fst[k]=tot;
}
}hsh;
ll solve(int x,int y,int k){
if (!x || !y || x>m || y>n) return 0;
ll ans=hsh.qry(x,y,k); if (ans) return ans;
int i,j,u,v; ll tmp=k?a[x]:b[y];
for (i=k; i<4; i+=2){
for (j=15,u=x+dx[i],v=y+dy[i]; j>=0; j--)
if (f[i][j][k?v:u]<=tmp){
u+=dx[i]*(1<=k) up(f[0][i][j],f[0][i-1][j-k]);
if (j+k<=m+1) up(f[2][i][j],f[2][i-1][j+k]);
}
for (j=0; j<=n+1; j++){
f[1][i][j]=f[1][i-1][j]; f[3][i][j]=f[3][i-1][j];
if (j>=k) up(f[1][i][j],f[1][i-1][j-k]);
if (j+k<=n+1) up(f[3][i][j],f[3][i-1][j+k]);
}
}
while (cas--){
scanf("%d%d",&i,&j);
printf("%lld\n",max(solve(i,j,0),solve(i,j,1))-1);
}
return 0;
}
一个显然的想法是用括号序列。然后考虑括号序列(x,y)->(x,y-x)。我们把y-x映射到一个幂函数1.03^k中。这样莫问令z为1.03^k中最小的>=y-x的数,然后我们让它y'=x+z(相当于在x的子树内添加无用点)。然后返回(x,z)即可。
AC代码如下:
#include
#include"City_lib.h"
#define ll long long
#define N 250009
using namespace std;
int tot,dfsclk,fst[N],pnt[N<<1],nxt[N<<1],a[N],lf[N],rg[N];
set S; map mp;
void init(){
int i,lim=1.03*(1<<19),cnt=0; S.clear(); mp.clear();
for (i=0; i<=lim; i=max(i+1.,i*1.03)){
mp[i]=cnt; a[cnt++]=i;
S.insert(i);
}
}
void add(int x,int y){
x++; y++;
pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void dfs(int x,int fa){
int i,y; lf[x]=++dfsclk;
for (i=fst[x]; i; i=nxt[i]){
y=pnt[i];
if (y!=fa) dfs(y,x);
}
rg[x]=dfsclk=lf[x]+(*S.lower_bound(dfsclk-lf[x]));
}
void Encode(int n,int a[],int b[]){
int i;
tot=0;
for (i=0; i>9,r=a[x&511],u=y>>9,v=a[y&511];
r+=l; v+=u;
if (u<=l && r<=v) return 0;
else return l<=u && v<=r?1:2;
}
标算是O(N^1.5logN)的,同时还有一个O(N^1.5)的做法,(另外似乎N^2能过)。我的做法是O(N^(5/3))的。
把所有部落按照龙的个数以N^(1/3)为界分类。考虑以部落x为射线起点。两头龙i->j和A,B相当相当于二者满足一个二维偏序关系,可以用一维排序二维数据结构。如果用树状数组的话,复杂度为N^(5/3)*logN,难以承受。考虑用O(N^0.5)修改,O(1)询问的分块,这样由于修改的总数是O(N),因此是O(N^1.5+N^(5/3))。
AC代码如下:
#include
#define ll long long
#define N 100009
using namespace std;
int n,m,pt,cnt,sz[N],last[N],nxt[N],ans1[N],ans2[N];
struct point{ int x,y; }w[N],A,B;
struct node{ point p; int pos,id; }p[N],q[N];
struct trp{ int x,y,k; bool bo; }a[N],a0[N];
point operator -(point u,point v){ return (point){u.x-v.x,u.y-v.y}; }
ll crs(point u,point v){ return (ll)u.x*v.y-(ll)u.y*v.x; }
bool cmp(node u,node v){
return u.pos0;
}
bool cmpx(trp u,trp v){ return u.x0){
p[i]=(node){w[i]-B,0,i};
q[i]=(node){w[i]-A,0,i};
p[i+n]=(node){A-w[i],1,i+n};
q[i+n]=(node){B-w[i],1,i+n};
} else{
p[i]=(node){w[i]-A,1,i};
q[i]=(node){w[i]-B,1,i};
p[i+n]=(node){B-w[i],0,i+n};
q[i+n]=(node){A-w[i],0,i+n};
}
cnt=n<<1;
sort(p+1,p+cnt+1,cmp); sort(q+1,q+cnt+1,cmp);
for (i=1; i<=cnt; i++){
a[p[i].id].x=a[q[i].id].y=i; a[i].bo=(i<=n);
}
for (i=1; i<=cnt; i++) a0[i]=a[i];
for (i=n; i; i--){
nxt[i]=last[a0[i].k]; last[a0[i].k]=i;
}
sort(a+1,a+cnt+1,cmpx);
int lim=pow(n,1./3); blk.init();
for (i=1; i<=pt; i++) if (sz[i]>lim){
memset(ans1,0,sizeof(ans1));
memset(ans2,0,sizeof(ans2));
blk.clr();
for (j=1; j<=cnt; j++)
if (a[j].bo && a[j].k==i) blk.ins(a[j].y);
else if (a[j].k!=i){
if (a[j].bo) ans1[a[j].k]+=blk.qry(a[j].y);
else{
tmp=blk.qry(a[j].y);
ans1[a[j].k]+=tmp; ans2[a[j].k]+=tmp;
}
}
blk.clr();
for (j=cnt,x=0; j; j--)
if (a[j].bo && a[j].k==i){
x++; blk.ins(a[j].y);
} else if (a[j].k!=i && a[j].bo)
ans2[a[j].k]+=x-blk.qry(a[j].y);
for (j=1; j<=pt; j++){
hsh.add(j,i,ans1[j]);
hsh.add(i,j,ans2[j]);
}
}
for (i=1; i<=m; i++){
if (hsh.len[i]!=-1){
printf("%d\n",hsh.len[i]); continue;
}
tmp=0;
for (x=last[hsh.px[i]]; x; x=nxt[x])
for (y=last[hsh.py[i]]; y; y=nxt[y])
if (a0[x].x>a0[y].x && a0[x].ya0[y].x && a0[x+n].y
2017.12.29