AT_agc003_e 题解

题目链接

神仙题,我会把我自己思考的过程一步步写出来。

初看这题时感觉没什么思路,所以随便算了点东西。很容易发现如果对于一个 iqiqi+1,那么 qi 就没有意义,每次把元素放进来时先把头部比它大的都弹走,再把它放进去,设处理完的 size 为 cnt。

然后就是这道题的精髓所在了!很容易就可以发现 qi+1 的串是 qi 串的弱循环。所以就可以弄一个 solve 函数,solve (x, y) 意思是处理第 q 个串循环 y 遍的贡献。运行 solve (cnt, 1) 就可以了,但是 solve 函数怎么设计呢?

以下设 qi=leni

前面的循环节可以用 solve (cnt - 1, len[cnt] / len[cnt - 1]) 处理,但是后面多余的呢?

这时我们发现需要改变 solve 函数了,设 solve (x, y) 为处理前 x 个字符循环 y 次的贡献。

转移首先要找到第一个 t,使得 len[t]<x,前面的部分仍然是循环,solve (len[t], x / len[t]) 就解决了,后面还有 x mod len[t] 个剩余的,这些只循环 1 次,solve (x % len[t], 1) 就可以了。

另外,注意下边界问题,如果 xn 了,那么前 x 个字符就是 1x,这些的出现次数都 +1,用差分维护就行。

分析一下时间复杂度,T(x)=x+T(x1)+T(x2)=x2,考虑优化。

时间主要用在了 xT(x1) 上,第一个用二分可以 logn。关于第二个,solve 函数都是 solve (len[t], x / len[t]) 的形式,第一个参数也就 n 种。因此,我们用一个桶 ff[t] 统计的是 1len[t] 个字符循环的次数(即出现在最终字符串中的次数)。最后再把 f 中的一起统计一下就行。

现在的 solve 函数就成了这样:

复制代码
void solve (int x, int y) {
    if (x <= n) {
        d[1] += y;
        d[x + 1] -= y;
        return;
   }
   int t = lower_bound (len + 1, len + cnt + 1, x) - len - 1;
   f[t] += x / a[t] * y;
   solve (x % a[t], y);
}
复制代码

 

结束后,我们倒序循环一下。f[i] 拆成 f[i1]len[i]len[i1]+len[i] mod len[i1],前面的加到 f[i1] 上,后面的继续用 solve 函数。(即 solve (len[i] % len[i - 1], f[i]) )。

此题的 qi 也可能小于 n,所以遇到这种情况,我们把 n 设为最小的 qi,输出时 >n 的输出 0 即可。

这样,时间复杂度降到了 nlog2n,另外注意开长整型和特判 q=0 的情况。

少量注释的代码:

复制代码
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
int n, q, cnt;
int a[100005], d[100005], f[100005];
void solve (int x, int y) {//求解前 x 个字符的贡献乘上 y 
    if (x <= n) {
        d[1] += y;
        d[x + 1] -= y;
        return;
    }
    int t = lower_bound (a + 1, a + cnt + 1, x) - a - 1;
    f[t] += x / a[t] * y;//其实是前 a[t] 个字符乘上 x / a[t] 加上剩余的。 
    solve (x % a[t], y);
}
signed main () {
    int x, z;
    cin >> n >> q;
    if (q == 0) {
        for (int i = 1; i <= n; i ++) cout << 1 << "\n";
        return 0;
    }
    z = n;
    a[++ cnt] = n;
    for (int i = 1; i <= q; i ++) {
        cin >> x;
        n = min (n, x);
        while (cnt != 0 && a[cnt] >= x) -- cnt;
        a[++ cnt] = x;
    }
    solve (x, 1);
    for (int i = cnt; i >= 2; i --) {
        f[i - 1] += a[i] / a[i - 1] * f[i];
        solve (a[i] % a[i - 1], f[i]);
    }
    d[1] += f[1];
    d[n + 1] -= f[1];
    for (int i = 1; i <= n; i ++) {
        d[i] += d[i - 1];
        cout << d[i] << "\n";
    }
    for (int i = n + 1; i <= z; i ++) cout << 0 << "\n";
    return 0;
}
复制代码

 

posted @   Xy_top  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示