BZOJ1035 : [ZJOI2008]Risk

首先要将这个图连通,方法是通过扫描线+set求出每个连通块最高的点上方的第一条边,然后向交点连边。

然后把边拆成两条双向边,每次找到一条没走过的边,找到极角排序后它的反向边的后继,直到回到这条边。根据叉积可以求出面积,如果面积非负,那么就说明找到了一个封闭区域。

然后再进行一次扫描线,找到一个点上方最低的边,即可完成点定位。

时间复杂度$O(m\log m)$。

#include<cstdio>
#include<cmath>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
const double eps=1e-8;
const int N=20010,M=50010;
int n,m,q,cnt,i,x,y;
map<int,int>T[20010];
inline int sgn(double x){
  if(fabs(x)<eps)return 0;
  return x>0?1:-1;
}
struct P{
  double x,y;
  P(){}
  P(double _x,double _y){x=_x,y=_y;}
  double operator*(const P&b){return x*b.y-y*b.x;}
}a[N],b[N];
struct E{
  int x,y;double o;
  E(){}
  E(int _x,int _y){x=_x,y=_y,o=atan2(a[y].x-a[x].x,a[y].y-a[x].y);}
}e[M];
bool del[M],ex[M];int from[M],id[N],f[N];
struct EV{
  double x;int y,t;
  EV(){}
  EV(double _x,int _y,int _t){x=_x,y=_y,t=_t;}
}ev[M<<1];
inline bool cmpEV(const EV&a,const EV&b){
  if(sgn(a.x-b.x))return a.x<b.x;
  return a.t<b.t;
}
namespace GetArea{
struct cmp{bool operator()(int a,int b){return e[a].o<e[b].o;}};
set<int,cmp>g[N];set<int,cmp>::iterator k;int i,j,q[M],t;
void work(){
  for(i=0;i<m+m;i++)if(!del[i]&&!ex[i]){
    for(q[t=1]=j=i;;q[++t]=j=*k){
      k=g[e[j].y].find(j^1);k++;
      if(k==g[e[j].y].end())k=g[e[j].y].begin();
      if(*k==i)break;
    }
    double s=0;
    for(j=1;j<=t;j++)s+=a[e[q[j]].x]*a[e[q[j]].y],del[q[j]]=1;
    if(sgn(s)<0)continue;
    for(cnt++,j=1;j<=t;j++)from[q[j]]=cnt;
  }
}
}
namespace ScanLine{
struct cmp{
  bool operator()(int A,int B){
    if(e[A].x==e[B].x)return e[A].o>e[B].o;
    double x=min(a[e[A].x].x,a[e[B].x].x),
           yA=(a[e[A].x].y-a[e[A].y].y)*(x-a[e[A].y].x)/(a[e[A].x].x-a[e[A].y].x)+a[e[A].y].y,
           yB=(a[e[B].x].y-a[e[B].y].y)*(x-a[e[B].y].x)/(a[e[B].x].x-a[e[B].y].x)+a[e[B].y].y;
    return yA>yB;
  }
};
set<int,cmp>T;
int cnt,i,j,k,g[M],v[M],nxt[M],ed,vis[N],t,tmp[N];
inline bool cmpC(int x,int y){return a[x].x<a[y].x;}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
  vis[x]=1;
  if(a[x].y>a[t].y)t=x;
  for(int i=g[x];i;i=nxt[i])if(!vis[v[i]])dfs(v[i]);
}
inline double cal(int A,double x){
  return(a[e[A].x].y-a[e[A].y].y)*(x-a[e[A].y].x)/(a[e[A].x].x-a[e[A].y].x)+a[e[A].y].y;
}
void connect(){
  for(i=0;i<m+m;i++)add(e[i].x,e[i].y);
  for(i=1;i<=n;i++)if(!vis[i])dfs(t=i),ev[cnt++]=EV(a[t].x,t,2);
  for(i=0;i<m+m;i++)if(sgn(a[e[i].x].x-a[e[i].y].x)>0){
    ev[cnt++]=EV(a[e[i].y].x,i,1);
    ev[cnt++]=EV(a[e[i].x].x,i,0);
  }
  sort(ev,ev+cnt,cmpEV);
  a[n+1]=P(10010,10010);
  a[n+2]=P(-10010,10010);
  e[m+m]=E(n+1,n+2);
  T.insert(m+m);
  e[m+m+1]=E(n+2,n+1);
  n+=2,m++;
  for(ed=0,i=1;i<=n;i++)g[i]=0;
  for(i=0;i<cnt;i++){
    if(ev[i].t==0)T.erase(ev[i].y);
    if(ev[i].t==1)T.insert(ev[i].y);
    if(ev[i].t==2){
      a[n+1]=P(ev[i].x,a[ev[i].y].y+eps);
      a[n+2]=P(ev[i].x-1,a[ev[i].y].y+eps);
      e[m+m]=E(n+1,n+2);
      T.insert(m+m);
      set<int,cmp>::iterator j=T.find(m+m);
      j--,add(*j,ev[i].y);
      T.erase(m+m);
    }
  }
  int newm=m+m;
  for(i=0;i<m+m;i++){
    for(cnt=0,j=g[i];j;j=nxt[j]){
      if(!sgn(a[v[j]].x-a[e[i].x].x)){
        e[newm++]=E(v[j],e[i].x);
        e[newm++]=E(e[i].x,v[j]);
        continue;
      }
      if(!sgn(a[v[j]].x-a[e[i].y].x)){
        e[newm++]=E(v[j],e[i].y);
        e[newm++]=E(e[i].y,v[j]);
        continue;
      }
      tmp[++cnt]=v[j];
    }
    if(!cnt)continue;
    ex[i]=ex[i^1]=1;
    sort(tmp+1,tmp+cnt+1,cmpC);
    for(k=e[i].y,j=1;j<=cnt;k=n,j++){
      a[++n]=P(a[tmp[j]].x,cal(i,a[tmp[j]].x));
      e[newm++]=E(k,n);
      e[newm++]=E(n,k);
      e[newm++]=E(tmp[j],n);
      e[newm++]=E(n,tmp[j]);
    }
    e[newm++]=E(n,e[i].x);
    e[newm++]=E(e[i].x,n);
  }
  m=newm/2;
}
void location(){
  for(i=cnt=0;i<m+m;i++)if(!ex[i]&&sgn(a[e[i].x].x-a[e[i].y].x)>0){
    ev[cnt++]=EV(a[e[i].y].x,i,1);
    ev[cnt++]=EV(a[e[i].x].x,i,0);
  }
  for(i=0;i<q;i++)ev[cnt++]=EV(b[i].x,i,2);
  sort(ev,ev+cnt,cmpEV);
  T.clear();
  for(i=0;i<cnt;i++){
    if(ev[i].t==0)T.erase(ev[i].y);
    if(ev[i].t==1)T.insert(ev[i].y);
    if(ev[i].t==2){
      a[n+1]=P(ev[i].x,b[ev[i].y].y);
      a[n+2]=P(ev[i].x-1,b[ev[i].y].y);
      e[m+m]=E(n+1,n+2);
      T.insert(m+m);
      set<int,cmp>::iterator j=T.find(m+m);
      if(j!=T.begin())j--,id[ev[i].y]=from[*j];
      T.erase(m+m);
    }
  }
}
}
inline int getid(){
  int x,y;
  scanf("%d%d",&x,&y);
  if(T[x+10000][y])return T[x+10000][y];
  T[x+10000][y]=++n;
  a[n]=P(x,y);
  return n;
}
bool g[610][610];
int main(){
  scanf("%d%d",&q,&m);
  for(i=0;i<q;i++)scanf("%lf%lf",&b[i].x,&b[i].y);
  for(i=0;i<m;i++){
    x=getid();
    y=getid();
    e[i<<1]=E(x,y);
    e[i<<1|1]=E(y,x);
  }
  ScanLine::connect();
  for(i=0;i<m+m;i++)if(!ex[i])GetArea::g[e[i].x].insert(i);
  GetArea::work();
  ScanLine::location();
  for(i=0;i<q;i++)f[id[i]]=i+1;
  for(i=0;i<m+m;i++)if(!ex[i])g[f[from[i]]][f[from[i^1]]]=1;
  for(i=1;i<=q;i++){
    for(x=g[i][i]=0,y=1;y<=q;y++)if(g[i][y])x++;
    printf("%d",x);
    for(y=1;y<=q;y++)if(g[i][y])printf(" %d",y);
    puts("");
  }
  return 0;
}

  

时间: 2024-11-13 08:04:59

BZOJ1035 : [ZJOI2008]Risk的相关文章

BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14982  Solved: 6081[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

bzoj1037[ZJOI2008]生日聚会

bzoj1037[ZJOI2008]生日聚会 题意: 一排小孩坐着玩游戏.就座的方案满足如下条件:对于任意连续的一段,男孩与女孩的数目之差不超过k.给出男孩数,女孩数和k,求就座方案数除以12345678的余数. 题解: dp方程见程序,i1i2表示当前选了几男几女,i3i4分别表示当前男比女多几个和女比男多几个. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define

Risk(最短路)

Risk Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 2915   Accepted: 1352 Description Risk is a board game in which several opposing players attempt to conquer the world. The gameboard consists of a world map broken up into hypothetical

BZOJ 1034: [ZJOI2008]泡泡堂BNB( 贪心 )

贪心...用最弱的赢最弱的,用最强的赢最强的,否则用最弱的和最强的比... (贴个官方题解:将双方的选手均按从强到弱排序,然后第一次扫描尽可能用当前剩下的选手中能赢对手当前最强选手中最弱的一个去赢得胜利,若无法做到,则暂时不考虑给对方最强的选手匹配对手.第二遍扫描使用同样策略去获取尽量多的平局.最后剩下的选手任意匹配就能获得最多的分数) -------------------------------------------------------------------- #include<cs

BZOJ 1040: [ZJOI2008]骑士( 树形dp )

这是一个森林中, 每棵树上都有一个环...每棵树单独处理, 找出环上任意一条边断开, 限制一下这条边两端点的情况, 然后就可以树dp了.. ------------------------------------------------------------------------ #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace

BZOJ 题目1036: [ZJOI2008]树的统计Count(Link Cut Tree,修改点权求两个最大值和最大值)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 8421  Solved: 3439 [Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

1036: [ZJOI2008]树的统计Count

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 7496  Solved: 3078[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 II

ZJOI2008泡泡堂BNB

1034: [ZJOI2008]泡泡堂BNB Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1305  Solved: 676[Submit][Status] Description 第XXXX届NOI期间,为了加强各省选手之间的交流,组委会决定组织一场省际电子竞技大赛,每一个省的代表队由n名选手组成,比赛的项目是老少咸宜的网络游戏泡泡堂.每一场比赛前,对阵双方的教练向组委会提交一份参赛选手的名单,决定了选手上场的顺序,一经确定,不得修改.比赛中