题解 P4704 【太极剑】
在正式写题解之前,先抨击一下已有的题解:
@Loi_bibo TLE
@Ofnoname
@yi_heng 说是
@lokiii 证明和结论都是错的,卡法见附注。
出题人的数据真弱
现在说我的解法:放心,下面两个都能通过,如果再次被卡可以用各种手段告知我
实际上前面几份题解虽然是错的,但找出最短距离并枚举这一段上的起点是必要的,这里要解决的是怎样用
不妨设最短点对是
首先断开弧
从该分割线起,所有边均指不包含弧
引理:设边
这是因为边
这样,我们可以过滤掉可以对答案无影响的边,这样就能按两端点同时增序枚举这些边。
定义
考虑相邻的两条边
我们考虑按编号从
其实还差一点:我们错误地忽略了包含弧
如果两端点都不在
如果都在,这是不可能的。
假定在
由于
代码:
#include<bits/stdc++.h>
using namespace std;
int read() {
char c=getchar();while(!isdigit(c))c=getchar();
int num=0;while(isdigit(c))num=num*10+c-'0',c=getchar();
return num;
}
int m[1000001];
int f[400001];
int main() {
int n = read();
int dis = n * 2, lp, rp;
for (int i = 1; i <= n; i++) {
int a, b;
a = read(), b = read();
if (a > b) swap(a, b);
m[a]=b, m[b]=a;
m[a+n*2]=b, m[b+n*2] = a;
if (b - a < dis)
dis = b - a, lp = a, rp = b;
if (a + n * 2 - b < dis)
dis = a + n * 2 - b, lp = b, rp = a + n * 2;
}
for (int i = 1; i <= n * 2; i++) m[i]=m[i+lp-1];
int d = rp - lp + 1;
int pt = 0, last = 1;
for (int i = d + 1; i <= n * 2; i++)
if (m[i] < i && m[i] > last) {
for (int j = last; j < m[i]; j++) f[j] = i - 1;
last = m[i];
}
int ans = n;
for (int i = 1; i < d; i++) {
if (m[i] > last) {
for (int j = last; j < m[i]; j++) f[j] = n * 2;
last = m[i];
}
int p = i, cnt = 0;
while (p) {
p = f[p];
++cnt;
}
ans = min(ans, cnt);
}
cout << (ans + 1) / 2 << endl;
}
附注:
1.卡掉@yi_heng的数据:
#include<bits/stdc++.h>
using namespace std;
int main() {
int n = 200000; //必须是偶数
cout << n << endl;
cout << 1 << ' ' << n / 2 + 1 << '\n';
cout << n + 1 << ' ' << n * 3 / 2 + 1 << endl;
int p = 2, q = n + 2;
while (p <= n / 2) cout << p++ << ' ' << q++ << endl;
p = n / 2 + 2, q = n * 3 / 2 + 2;
while (p <= n) cout << p++ << ' ' << q++ << endl;
}
2.卡掉@lokiii的数据:
10
1 6
5 10
11 16
15 20
2 9
3 8
4 7
12 19
13 18
14 17
正解:1,输出:2