题目描述

$ZZQ$ 是一国之主。 这个国家有$N$个城市, 第$i$个城市与第$(i + 1) (mod N)$和$(i - 1) (mod N)$在一个正$N$边形相连。

$ZZQ$ 又新建了$N - 3$条道路。这些道路都是连接两个城市的直线 段,且任意两条线段都只可能在城市处相交,不会在旧的道路上新建 道路。同时$ZZQ$钦定任何一条新旧道路通行都只需要花费$1$天时间。

建设完成后,$ZZQ$开始了$M$次旅行,她想知道每次旅行最少需要 花费多少天。

输入输出格式

输入格式:

第一行输入 一个 个正整数$N$,表示城市个数。

接下来$N - 3$行,每行两个整数$u, v$,表示这条道路连接的两个城市。

接下来一个整数$M$,表示旅行次数。

接下来$M$行,每行两个整数$u, v$表示这次旅行的起点和终点。

保证起点和终点不同。

输出格式:

对于每个询问,输出一行一个整数$ans$表示答案。


其实这题在$BZOJ$上有原题:$BZOJ - 4449$

首先这显然是一个平面图,然后正解是平面图转对偶图 + 点分治,对偶图是什么?我不知道啊。

然后对偶图就是将平面图的面看作点,然后相邻的面之间连边,其中有$N - 3$条边,$N - 2$个点,故对偶图显然是一棵树。

其中,要建对偶图的话,每次选择$Deg$为$2$的点,再处理一下三角剖分,就能知道有哪些面相连了。

然后就在树上点分治。

至于过程,考虑一条原图的新加的边,如果某个询问的两个点恰好跨过它,那么就一遍$BFS$就好了,否则找到它们所在的被此时直线切割形成的两个块的左块或右块进行分治,于是可以使用点分治。

那么如何处理多个询问有哪几个横跨了当前点分治中心呢?只需将询问编号与当前分治中心用链式前向星连起来,访问到当前中心时扫一遍即可,当然这是需要下传的。

复杂度$O(NlogN)$。

代码:

 #pragma GCC optimize ("O3")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm> using namespace std; const int MAXN = 1e05 + ;
const int MAXM = 1e05 + ;
const int MAXQ = 1e05 + ; const int INF = 0x7fffffff; struct LinkedForwardStar {
int to;
bool ban; int next;
} ; LinkedForwardStar Link[MAXM << ];
int Head[MAXN]= {};
int size = ; void Insert (int u, int v) {
Link[++ size].to = v;
Link[size].ban = false;
Link[size].next = Head[u]; Head[u] = size;
} vector<int> Graph[MAXN]; struct Edge {
int u, v;
int index; Edge () {} Edge (int fu, int fv, int find) :
u (fu), v (fv), index (find) {} bool operator < (const Edge& p) const {
if (u == p.u)
return v < p.v;
return u < p.u;
}
} ; Edge Edges[MAXN * ];
int edges = ;
int ind = ; int Triangle[MAXN][]; void AddEdge (int x, int y, int z) { // 处理三角剖分
ind ++;
Triangle[ind][] = x;
Triangle[ind][] = y;
Triangle[ind][] = z;
Edges[++ edges] = Edge (min (x, y), max (x, y), ind);
Edges[++ edges] = Edge (min (x, z), max (x, z), ind);
Edges[++ edges] = Edge (min (y, z), max (y, z), ind);
// cout << "Test: " << x << ' ' << y << ' ' << z << endl;
} int N, M; int Deg[MAXN]= {};
bool Vis[MAXN]= {false}; queue<int> Que; void Toposort () {
for (int i = ; i <= N; i ++)
if (Deg[i] == )
Que.push(i); while (! Que.empty()) {
int u = Que.front();
Que.pop(); if (Deg[u] != )
continue;
Vis[u] = true; int x = , y;
for (int i = ; i < (int) Graph[u].size(); i ++) {
int v = Graph[u][i];
if (Vis[v])
continue; ! x ? x = v : y = v;
Deg[v] --;
if (Deg[v] == )
Que.push(v);
}
AddEdge (u, x, y);
}
} void Construct () {
sort (Edges + , Edges + edges + );
for (int i = ; i < edges; i ++)
if (Edges[i].u == Edges[i + ].u && Edges[i].v == Edges[i + ].v) {
Insert (Edges[i].index, Edges[i + ].index);
Insert (Edges[i + ].index, Edges[i].index);
}
} int Size[MAXN]= {}; int tot; int minval = INF, gravity;
void Get_Gravity (int root, int father) {
Size[root] = ;
int maxpart = ;
for (int i = Head[root]; i; i = Link[i].next) {
int v = Link[i].to;
if (v == father || Link[i].ban)
continue; Get_Gravity (v, root);
Size[root] += Size[v];
maxpart = max (maxpart, Size[v]);
}
maxpart = max (maxpart, tot - Size[root]); if (maxpart < minval) {
minval = maxpart;
gravity = root;
}
} LinkedForwardStar Qlink[MAXQ << ];
int Qhead[MAXN];
int qsize = ; void Qinsert (int u, int v) {
Qlink[++ qsize].to = v;
Qlink[qsize].next = Qhead[u]; Qhead[u] = qsize;
} int Answer[MAXQ]= {}; int Dist[][MAXN]= {}; int cur = ; int Rec[MAXN * ];
int Belong[MAXN], Last[MAXN];
// Belong[]记录所属子树,Last[]记录分治中心编号(为了防止BFS越过当前分支中心管理子树) int cnt = ; void BFS (int S, int type) {
while (! Que.empty())
Que.pop();
for (int i = ; i <= cnt; i ++)
Dist[type][Rec[i]] = INF; Que.push(S);
Dist[type][S] = ; while (! Que.empty()) {
int u = Que.front();
Que.pop(); for (int i = ; i < (int) Graph[u].size(); i ++) {
int v = Graph[u][i];
if (Dist[type][v] < INF || Last[v] < cur)
continue; Dist[type][v] = Dist[type][u] + ;
Que.push(v);
}
}
} void DFS (int root, int father, int top) {
for (int j = ; j < ; j ++) {
Rec[++ cnt] = Triangle[root][j];
Belong[Triangle[root][j]] = top;
Last[Triangle[root][j]] = cur;
} for (int i = Head[root]; i; i = Link[i].next) {
int v = Link[i].to;
if (v == father || Link[i].ban)
continue;
DFS (v, root, top);
}
} pair<int, int> Queries[MAXQ]; int Query[MAXQ]; void Solve (int root) {
if (! Qhead[root])
return ; tot = Size[root];
minval = INF;
Get_Gravity (root, );
cur ++, cnt = ;
for (int i = Head[gravity]; i; i = Link[i].next) {
int v = Link[i].to;
if (Link[i].ban)
continue;
DFS (v, gravity, v);
}
for (int j = ; j < ; j ++) {
Rec[++ cnt] = Triangle[gravity][j];
Belong[Triangle[gravity][j]] = gravity;
Last[Triangle[gravity][j]] = cur;
} for (int j = ; j < ; j ++)
BFS (Triangle[gravity][j], j);
cnt = ;
for (int i = Qhead[root]; i; i = Qlink[i].next) {
int v = Qlink[i].to;
Query[++ cnt] = v;
}
Qhead[root] = ; for (int i = ; i <= cnt; i ++) {
int u = Queries[Query[i]].first, v = Queries[Query[i]].second;
if (Belong[u] == Belong[v]) {
if (Belong[u] == gravity)
Answer[Query[i]] = ; // 在同一个三角剖分中
else
Qinsert (Belong[u], Query[i]);
}
else {
for (int j = ; j < ; j ++)
Answer[Query[i]] = min (Answer[Query[i]], Dist[j][u] + Dist[j][v]);
}
} for (int i = Head[gravity]; i; i = Link[i].next) {
int v = Link[i].to;
if (! Link[i].ban) {
Link[i ^ ].ban = true;
Solve (v);
}
}
} int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int main () {
// freopen ("city.in", "r", stdin); N = getnum (); for (int i = ; i <= N; i ++)
Dist[][i] = Dist[][i] = Dist[][i] = INF; for (int i = ; i < N; i ++) {
Graph[i].push_back(i + );
Graph[i + ].push_back(i);
Deg[i] ++, Deg[i + ] ++;
}
Graph[].push_back(N), Graph[N].push_back();
Deg[] ++, Deg[N] ++;
for (int i = ; i <= N - ; i ++) {
int u, v;
u = getnum (), v = getnum ();
u ++, v ++;
Graph[u].push_back(v);
Graph[v].push_back(u);
Deg[u] ++, Deg[v] ++;
} Toposort ();
Construct (); M = getnum (); for (int i = ; i <= M; i ++) {
int u, v;
u = getnum (), v = getnum ();
u ++, v ++;
if (u != v) {
Queries[i] = make_pair (u, v);
Qinsert (, i); // 一开始所有询问都位于根节点的子树中
Answer[i] = INF;
}
} Size[] = N;
Solve (); for (int i = ; i <= M; i ++)
printf ("%d\n", Answer[i]); return ;
} /*
6
0 2
0 3
0 4
4
1 5
3 0
4 5
2 4
*/

NOIP模拟赛 城市的更多相关文章

  1. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  2. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

  3. NOIP模拟赛 by hzwer

    2015年10月04日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...

  4. 大家AK杯 灰天飞雁NOIP模拟赛题解/数据/标程

    数据 http://files.cnblogs.com/htfy/data.zip 简要题解 桌球碰撞 纯模拟,注意一开始就在袋口和v=0的情况.v和坐标可以是小数.为保险起见最好用extended/ ...

  5. 队爷的讲学计划 CH Round #59 - OrzCC杯NOIP模拟赛day1

    题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的讲学计划 题解:刚开始理解题意理解了好半天,然后发 ...

  6. 队爷的Au Plan CH Round #59 - OrzCC杯NOIP模拟赛day1

    题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的Au%20Plan 题解:看了题之后觉得肯定是DP ...

  7. 队爷的新书 CH Round #59 - OrzCC杯NOIP模拟赛day1

    题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的新书 题解:看到这题就想到了 poetize 的封 ...

  8. CH Round #58 - OrzCC杯noip模拟赛day2

    A:颜色问题 题目:http://ch.ezoj.tk/contest/CH%20Round%20%2358%20-%20OrzCC杯noip模拟赛day2/颜色问题 题解:算一下每个仆人到它的目的地 ...

  9. CH Round #52 - Thinking Bear #1 (NOIP模拟赛)

    A.拆地毯 题目:http://www.contesthunter.org/contest/CH%20Round%20%2352%20-%20Thinking%20Bear%20%231%20(NOI ...

随机推荐

  1. C++解析(4):引用的本质

    0.目录 1.引用的意义 2.特殊的引用 3.引用的本质 4.函数返回引用 5.小结 1.引用的意义 引用作为变量別名而存在,因此在一些场合可以代替指针 引用相对于指针来说具有更好的可读性和实用性 注 ...

  2. P1053 篝火晚会

    题目描述 佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”.在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会.一共有nnn个同学,编号从111到nnn.一开始 ...

  3. atcoder 2017Code festival C ——D题 Yet Another Palindrome Partitioning(思维+dp)

    题目大意: 把一个字符串s分割成m个串,这m个串满足至多有一种字符出现次数为奇数次,其他均为偶数次,问m的最小值 题解: 首先我们想一下纯暴力怎么做 显然是可以n^2暴力的,然后dp[i]表示分割到i ...

  4. Cornfields POJ - 2019(二维RMQ板题)

    就是求子矩阵中最大值与最小值的差... 板子都套不对的人.... #include <iostream> #include <cstdio> #include <sstr ...

  5. 【BZOJ3555】企鹅QQ(字符串哈希)

    [BZOJ3555]企鹅QQ(字符串哈希) 题面 BZOJ 题解 把前缀哈希一下,后缀哈希一下 枚举哪个位置不选,然后检查一下相同就行了.. 为什么我的\(Hash\)老是\(WA\), 为什么\(Z ...

  6. Unity3D for VR 学习(4): 自绘摄像机的视口区域锥体

    在Unity Editor下,当选择Camera组件后,可呈现出Camera视口区域锥体,非常方便.但是当选择其他物体,如Cube后,就无法得知是否在Camera市口区内了,这里我找到了雨松MOMO的 ...

  7. 驱动之LCD的介绍与应用20170209

    本文主要介绍的是LCD的介绍与应用,直接看个人笔记即可:

  8. 洛谷P1991 无线通讯网

    P1991 无线通讯网 170通过 539提交 题目提供者洛谷OnlineJudge 标签图论 难度普及+/提高 提交该题 讨论 题解 记录 最新讨论 怎么又炸了 为啥一直40!求解! UKE:inv ...

  9. Java中String的替换函数:replace与replaceAll的区别

    例如有如下x的字符串  String x = "[kllkklk\\kk\\kllkk]"; 要将里面的"kk"替换为++,可以使用两种方法得到相同的结果  r ...

  10. 全表 or 索引

    这一篇文章证实了以前对MySQL优化程序的工作原理. MySQL就像一个人一样,总是聪明的去选择当前最快的方式去查询,而不是像Oracle数据那样死板地根据规格去查询. 查询的要求在于快.而对于数据库 ...