CF755D 数学规律法 解题报告

题目传送门

这道题怎么都是数据结构题解?那本蒟蒻就来一篇数学题解(貌似是本题唯一的线性做法)。

思路:

模拟赛上有一道十分相似的题目,但是一开始想到的是用数据结构维护每次插入一条线段之前查询有多少根线段与其相交,但是太蒻了不会打,所以只好思考数学解法。

注意到本题有两句特别关键的话:

  1. 任意三个对角线都不能在同一点相交;
  2. \(k\)\(n\) 互质。

第一句话保证了省去了许多需要讨论的情况,从而得出一个结论:

增加的截⾯数 \(=\) 新边与之前连的边的交点数 \(+1\)。(这里只算图形内的交点)

而第二句话告诉我们:每个点都会被连且仅会被连一次,这意味着不需要考虑重边的问题。

观察了一会儿样例,发现新截面数的增长很有规律:每次加的数 \(d\) 是单调递增并且逐步 \(+1\) 的,所以不妨从此入手,猜测增长速率 \(d\) 的增长规律。

画图举例,假设 \(n = 7,k = 3\)

先画第一条线段:

这时分割为了 \(2\) 份。

再画一条:

这时变成了 \(3\) 份,注意到这时只增加了 \(1\),而第一次增长了 \(2\),所以考虑将画第一条时单独提出来特殊考虑,作为起始状态。

再画一条呢?

会发现这时出现了一个交点,那么增长量将变为 \(2\)

这时我猜测每过一圈 \(d\) 就增加 \(1\)

再画一条:

交点又增加了一个!那么我们上面的假设就被否决了!这时我们提出新的规律:每经过一圈,\(d\) 将在接下来的两次操作中逐渐增加 \(1\)

但是很快我发现这个结论也是错的,当 \(n = 17,k = 3\) 时就可以推翻,因为将 \(16\) 号点和 \(2\) 号点时 \(d\) 已经增加 \(1\) 了,因为必定会和 \(1\) 号点连出去的线段相交。

那么经过一堆完善后,设现在分割的份数为 \(base\),结论如下:

  1. 若当前连的边未跨过 \(n\) 号点,\(base\leftarrow base + d\)
  2. 否则若当前连的边的较大的端点刚好为 \(n\)\(base\leftarrow base + d\) 并且 \(d\) 将在本次操作后的两次操作内逐渐增大 \(1\)
  3. 否则就只剩下较大的端点超过 \(n\) 的情况了,先 \(d\leftarrow d + 1\),再 \(base \leftarrow base + d\),并且 \(d\) 将在本次操作后的一次操作内逐渐增大 \(1\)

还没结束,万一 \(k\ge \frac{n}{2}\) 呢?这会直接导致 \(d\) 的增长过程还没结束就会再次接受增长的信号吗?

实际上,我们不难发现连边是具有对称性的,所以若 \(k\ge \frac{n}{2}\) 直接令 \(k\leftarrow n - k\) 就行了。

代码没什么好讲的,就是根据结论模拟,时间复杂度 \(O(n)\)

代码是直接由考场代码改过来的。

\(\texttt{Code:}\)

#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;
}
posted @ 2024-11-27 20:40  Brilliant11001  阅读(31)  评论(0)    收藏  举报