CF755D 数学规律法 解题报告
Brilliant11001 · · 题解
更好的阅读体验
NOIP 前写题解加 rp(
题目传送门
这道题怎么都是数据结构题解?那本蒟蒻就来一篇数学规律法题解(貌似是本题唯一的线性做法)。
思路:
模拟赛上有一道十分相似的题目,但是一开始想到的是用数据结构维护每次插入一条线段之前查询有多少根线段与其相交,但是太蒻了不会打,所以只好思考数学解法。
让我们充分发扬人类智慧
注意到本题有两句特别关键的话:
- 任意三个对角线都不能在同一点相交;
-
第一句话保证了省去了许多需要讨论的情况,从而得出一个结论:
增加的截⾯数
= 新边与之前连的边的交点数+1 。(这里只算图形内的交点)
而第二句话告诉我们:每个点都会被连且仅会被连一次,这意味着不需要考虑重边的问题。
观察了一会儿样例,发现新截面数的增长很有规律:每次加的数
画图举例,假设
先画第一条线段:
这时分割为了
再画一条:
这时变成了
再画一条呢?
会发现这时出现了一个交点,那么增长量将变为
这时我猜测每过一圈
再画一条:
交点又增加了一个!那么我们上面的假设就被否决了!这时我们提出新的规律:每经过一圈,
但是很快我发现这个结论也是错的,当
那么经过一堆完善后,设现在分割的份数为
- 若当前连的边未跨过
n 号点,base\leftarrow base + d ; - 否则若当前连的边的较大的端点刚好为
n ,base\leftarrow base + d 并且d 将在本次操作后的两次操作内逐渐增大1 ; - 否则就只剩下较大的端点超过
n 的情况了,先d\leftarrow d + 1 ,再base \leftarrow base + d ,并且d 将在本次操作后的一次操作内逐渐增大1 。
还没结束,万一
实际上,我们不难发现连边是具有对称性的,所以若
代码没什么好讲的,就是根据结论模拟,时间复杂度
代码是直接由考场代码改过来的。
#include <set>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1000010;
typedef long long ll;
int T;
int n, k;
void solve() {
scanf("%d%d", &n, &k);
if(k > n / 2) k = n - k;
int x = k % n + 1, lasx = 1;
ll res = 0, base = 2;
int d = 1;
int flag = -1; // flag 表示还有几次操作后 d 会 +1,相当于一个延时标记
for(int i = 1; i < n; i++) {
printf("%lld " , base);
if(~flag) flag--;
if(!flag) d++, flag = -1;
if(x + k > n && flag == -1) {
if(i != n - 1) d++;
base += d;
flag = 1;
lasx = x, x = (x + k - 1) % n + 1;
}
else if(x + k == n) {
base += d;
d++;
flag = 2;
lasx = x, x = (x + k - 1) % n + 1;
}
else {
base += d;
lasx = x, x = (x + k - 1) % n + 1;
}
res ^= (ll)i * base;
}
printf("%lld\n", base);
}
int main() {
T = 1;
while(T--) {
solve();
}
return 0;
}