HDU 2020 多校第一场 游记

和邓老师、戴老师,以及 JS A 队爷 lqs2015 四排了一场多校,感觉很爽。

可惜的一点就是 1012 的精度问题,最后都没过,说不定是数据问题呢(大嘘)……不然就是 8 题队了。

还有一个可惜的一点就是 1010 djq 的做法被卡常了,也是有点可惜吧。

大概整理一下题目(有些难题未完待续)。

1003 Cookies

链接

吐槽

被邓老师开场秒了 orz……一血

题解

强行二合一题,显然二分可以直接算出第 k k 个(也可以线性直接算,不过无所谓了),唯一的难度在于求 f f

好像目前没发现啥好办法,直接分段打表即可。多余的部分可以 O ( n log n ) O(n \log n) 筛,可以通过。

代码太长就不贴了 23333.

1004 Distinct Sub-palindromes

链接

题解

签到题,不难算出 n = 1 n=1 时答案为 26 26 n = 2 n=2 时答案为 676 676 n = 3 n=3 时答案为 17576 17576 2 6 3 26^3 ), n 4 n \ge 4 时答案为 15600 15600 26 × 25 × 24 26\times25\times24 )。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }

const int MAXN = 100005, MOD = 998244353;

int main() {
    int T; scanf("%d", &T);
    while (T--) {
        int n; scanf("%d", &n);
        if (n == 1) puts("26");
        else if (n == 2) puts("676");
        else if (n == 3) puts("17576");
        else puts("15600");
    }
    return 0;
}

1005 Fibonacci Sum

链接

题解

稍微难一点的签到题,注意到 m o d 1 0 9 + 9 \bmod 10^9+9 5 5 有二次剩余,可以直接写出斐波那契数列的通项公式。

f i b n = p ( a n b n ) fib_n=p\left(a^n-b^n\right)

那么随便推一推就行了。

p K i = 0 n ( a i c + b i c ) K = p K i = 0 n j = 0 K ( 1 ) K j a i j c b i ( K j ) c ( K j ) = p K j = 0 K ( 1 ) K j ( K j ) i = 0 n ( a j c b ( K j ) c ) i = p K j = 0 K ( 1 ) K j ( K j ) ( a j c b ( K j ) c ) n + 1 1 a j c b ( K j ) c 1 p^K\sum_{i=0}^n (a^{ic}+b^{ic})^K \\ = p^K\sum_{i=0}^n\sum_{j=0}^K(-1)^{K-j}a^{ijc}b^{i(K-j)c}\binom{K}{j} \\ = p^K\sum_{j=0}^K(-1)^{K-j}\binom{K}{j}\sum_{i=0}^n(a^{jc}b^{(K-j)c})^i \\ = p^K\sum_{j=0}^K(-1)^{K-j}\binom{K}{j}\frac{(a^{jc}b^{(K-j)c})^{n+1}-1}{a^{jc}b^{(K-j)c}-1}

所有东西都可以线性预处理(分母可以离线线性处理逆元,注意特判分母为 1 1 的情况)。不过听说带 log \log 也能过。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }

const int MOD = 1e9 + 9, S = 383008016, MAXN = 100005;
LL modpow(LL a, LL b) {
    LL res = 1;
    for (; b; b >>= 1) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
    }
    return res;
}
const int P = modpow(S, MOD - 2), A = (LL)(S + 1) * (MOD + 1) / 2 % MOD, B = 1 + MOD - A;
LL fac[MAXN], inv[MAXN], pa[MAXN], pb[MAXN], pan[MAXN], pbn[MAXN];
LL tmp[MAXN], fm[MAXN], pre[MAXN];
void init() {
    int n = 1e5;
    for (int i = fac[0] = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % MOD;
    inv[n] = modpow(fac[n], MOD - 2);
    for (int i = n; i > 0; i--)
        inv[i - 1] = inv[i] * i % MOD;
}

int main() {
    init();
    int T; scanf("%d", &T);
    while (T--) {
        LL n, c; int K;
        scanf("%lld%lld%d", &n, &c, &K);
        LL ac = modpow(A, c), acn = modpow(ac, n + 1);
        LL bc = modpow(B, c), bcn = modpow(bc, n + 1);
        for (int i = pa[0] = pb[0] = pan[0] = pbn[0] = 1; i <= K; i++) {
            pa[i] = pa[i - 1] * ac % MOD, pan[i] = pan[i - 1] * acn % MOD;
            pb[i] = pb[i - 1] * bc % MOD, pbn[i] = pbn[i - 1] * bcn % MOD;
        }
        LL mul = 1;
        for (int i = 0; i <= K; i++) {
            tmp[i] = (pa[i] * pb[K - i] - 1) % MOD;
            if (tmp[i]) mul = mul * tmp[i] % MOD;
            pre[i] = mul;
        }
        mul = modpow(mul, MOD - 2);
        memset(fm, 0, sizeof(fm));
        for (int i = K; i >= 0; i--) {
            fm[i] = (i ? pre[i - 1] : 1) * mul % MOD;
            if (tmp[i]) mul = mul * tmp[i] % MOD;
        }
        LL ans = 0;
        for (int i = 0; i <= K; i++) {
            LL t = inv[i] * inv[K - i] % MOD;
            if ((K - i) & 1) t = MOD - t;
            if (!tmp[i]) t = (n + 1) % MOD * t % MOD;
            else t = (pan[i] * pbn[K - i] - 1) % MOD * t % MOD * fm[i] % MOD;
            ans += t;
        }
        printf("%lld\n", ans % MOD * fac[K] % MOD * modpow(P, K) % MOD);
    }
    return 0;
}

1006 Finding a MEX

链接

题解

本来是中档题的,结果听说 O ( n n log n ) O(n \sqrt n \log n) 能过,就有点签到了……

显然套路地分大小点讨论,大点不超过 O ( n ) O(\sqrt n) 个,每次暴力枚举相邻的大点暴力修改。这里用 bit 或者线段树维护的话就带 log \log ,但是我们可以对于每个大点,分块维护每块内数字是否都出现过,以及每个数字的出现次数。这样查询就 O ( n ) O(\sqrt n) ,修改就 O ( 1 ) O(1) 了。注意到每个点的答案 \le 度数,因此总空间还是 O ( n ) O(n) 的。

小点的话每次直接暴力询问即可。总复杂度 O ( n n ) O(n \sqrt n)

开的 vector 有点随便,所以跑得比较慢……

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }

const int MAXN = 100005, B = 512;
int tim, T, n, m, Q, arr[MAXN], vis[MAXN], deg[MAXN];
vector<int> edge[MAXN], val[MAXN], tag[MAXN], big[MAXN];

int main() {
    for (scanf("%d", &T); T--;) {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++)
            scanf("%d", arr + i);
        for (int i = 1; i <= m; i++) {
            int u, v; scanf("%d%d", &u, &v);
            edge[u].push_back(v);
            edge[v].push_back(u);
            ++deg[u], ++deg[v];
        }
        for (int i = 1; i <= n; i++) {
            if (deg[i] > B) {
                val[i].resize(deg[i] + 1);
                tag[i].resize(deg[i] / B + 1);
                for (int j = 0; j <= deg[i] / B; j++)
                    tag[i][j] = min(j + B, (int)val[i].size()) - j;
                for (int j : edge[i]) {
                    if (deg[j] > B) big[i].push_back(j);
                    if (arr[j] <= deg[i])
                        if (!val[i][arr[j]]++) --tag[i][arr[j] / B];
                }
            } else {
                for (int j : edge[i])
                    if (deg[j] > B) big[i].push_back(j);
            }
        }
        scanf("%d", &Q);
        while (Q--) {
            int t, u, x; scanf("%d%d", &t, &u);
            if (t == 1) {
                scanf("%d", &x);
                int y = arr[u];
                for (int i : big[u]) {
                    if (y <= deg[i])
                        if (!--val[i][y]) ++tag[i][y / B];
                    if (x <= deg[i])
                        if (!val[i][x]++) --tag[i][x / B];
                }
                arr[u] = x;
            } else if (deg[u] <= B) {
                ++tim;
                for (int i : edge[u])
                    if (arr[i] <= deg[u]) vis[arr[i]] = tim;
                for (int i = 0;; i++) if (vis[i] != tim)
                    { printf("%d\n", i); break; }
            } else {
                for (int i = 0; i < (int)tag[u].size(); i++) if (tag[u][i]) {
                    for (int j = i * B;; j++) if (!val[u][j])
                        { printf("%d\n", j); break; }
                    break;
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            edge[i].clear(), val[i].clear();
            tag[i].clear(), big[i].clear();
            deg[i] = arr[i] = 0;
        }
    }
    return 0;
}

1007 Hunting Monsters

这题比较复杂,我专门写了一个文章

1008 Integral Calculus

链接

吐槽

这个是现场在戴老师、wolframalpha、OEIS 的共同帮助下切掉的题。orz djqls!

题解

首先 wolframalpha 可以找出:

0 x 2 n 1 e 1 / x 1 d x = ( 2 n 1 ) ! ζ ( 2 n ) \int_0^\infty \frac{x^{-2n-1}}{e^{1/x}-1}dx=(2n-1)!\zeta(2n)

其中 ζ \zeta 是黎曼函数(邓老师好像推出了一个递推形式可能能够证明)。

众所周知 ζ ( 2 n ) = k π 2 n \zeta(2n)=k\pi^{2n} k k 是有理数。现在的问题就落到了求 k k 上。

于是接下来伟大的欧拉 djq 就出场了——

考察 sin x = x i = 1 ( 1 x 2 i 2 π 2 ) \displaystyle \sin x=x \prod_{i=1}^\infty (1-\frac{x^2}{i^2\pi^2}) ,大概原因是因为 sin x \sin x 的根为 2 i π 2i\pi

于是再泰勒展开 sin x \sin x ,记 y = x 2 y=x^2 就有:

i = 0 ( 1 ) i y i ( 2 i + 1 ) ! = i = 1 ( 1 y i 2 π 2 ) \sum_{i=0}^\infty (-1)^i\frac{y^i}{(2i+1)!}=\prod_{i=1}^\infty (1-\frac{y}{i^2\pi^2})

我们考虑求上式的所有根的 n -n 次方和就是 ζ ( 2 n ) π 2 n \displaystyle \frac{\zeta(2n)}{\pi^{2n}} ,于是现在的问题就落到了求根的 n -n 次方和上了。

考虑多项式 F = i ( x a i ) F=\prod_i (x-a_i) ,那么对其取 ln \ln 后暴力展开,发现第 n n 项的系数即为根的 n -n 次方和乘以 1 n \frac{1}{n}

所以就非常明了了,写一个任意模数多项式 ln \ln 即可(我从来没写过所以贴了个别人的板子)。复杂度 O ( n log n ) O(n \log n)

#include <bits/stdc++.h>
#define LL long long

const int MAXN=6e5+7;
const double pi=acos(-1);
const LL MOD=1e9+9;
using namespace std;

int len,r[MAXN];
LL f[MAXN],g[MAXN],c[MAXN];
LL fac[MAXN], inv[MAXN], A[MAXN], B[MAXN], C[MAXN];
int n, T;

struct rec{
    double x,y;
}a[MAXN],b[MAXN],w[MAXN],dfna[MAXN],dfnb[MAXN],dfnc[MAXN],dfnd[MAXN];

rec operator +(rec a,rec b)
{
    return (rec){a.x+b.x,a.y+b.y};
}

rec operator -(rec a,rec b)
{
    return (rec){a.x-b.x,a.y-b.y};
}

rec operator *(rec a,rec b)
{
    return (rec){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}

rec operator !(rec a)
{
    return (rec){a.x,-a.y};
}

void fft(rec *a,int f)
{
    for (int i=0;i<len;i++)
    {
        if (i<r[i]) swap(a[i],a[r[i]]);
    }
    w[0]=(rec){1,0};
    for (int i=2;i<=len;i*=2)
    {
        rec wn=(rec){cos(2*pi/i),f*sin(2*pi/i)};
        for (int j=i/2;j>=0;j-=2) w[j]=w[j/2];
        for (int j=1;j<i/2;j+=2) w[j]=w[j-1]*wn;
        for (int j=0;j<len;j+=i)
        {
            for (int k=0;k<i/2;k++)
            {
                rec u=a[j+k],v=w[k]*a[j+k+i/2];
                a[j+k]=u+v;
                a[j+k+i/2]=u-v;
            }
        }
    }
}

void FFT(LL *x,LL *y,LL *z,LL n,LL m)
{
    len=1;
    while (len<n+m) len*=2;
    int k=trunc(log(len+0.5)/log(2));
    for (int i=0;i<len;i++)
    {
        r[i]=(r[i>>1]>>1)|((i&1)<<(k-1));
    }
    for (int i=0;i<len;i++)
    {
        LL A,B;
        if (i<n) A=x[i]; else A=0;
        if (i<m) B=y[i]; else B=0;   
        a[i]=(rec){A>>15,A&32767};
        b[i]=(rec){B>>15,B&32767};
    }
    fft(a,1); fft(b,1);
    for (int i=0;i<len;i++)
    {
        int j=(len-1)&(len-i);
        rec da,db,dc,dd;
        da=(a[i]+(!a[j]))*(rec){0.5,0};
        db=(a[i]-(!a[j]))*(rec){0,-0.5};
        dc=(b[i]+(!b[j]))*(rec){0.5,0};
        dd=(b[i]-(!b[j]))*(rec){0,-0.5};
        dfna[i]=da*dc;
        dfnb[i]=da*dd;
        dfnc[i]=db*dc;
        dfnd[i]=db*dd;
    }
    for (int i=0;i<len;i++)
    {
        a[i]=dfna[i]+dfnb[i]*(rec){0,1};
        b[i]=dfnc[i]+dfnd[i]*(rec){0,1};
    }
    fft(a,-1); fft(b,-1);
    for (int i=0;i<len;i++)
    {
        LL da,db,dc,dd;
        da=(LL)(a[i].x/len+0.5)%MOD;
        db=(LL)(a[i].y/len+0.5)%MOD;
        dc=(LL)(b[i].x/len+0.5)%MOD;
        dd=(LL)(b[i].y/len+0.5)%MOD;
        z[i]=((da<<30)%MOD+((db+dc)<<15)%MOD+dd)%MOD;
    }
}

LL power(LL x,LL y)
{
    if (y==1) return x;
    LL c=power(x,y/2);
    c=(c*c)%MOD;
    if (y%2) c=(c*x)%MOD;
    return c;
}

void solve(LL *f,LL *g,int d)
{
    if (d==1)
    {
        g[0]=power(f[0],MOD-2);
        return;
    }
    int mid=(d+1)/2;
    solve(f,g,mid); 
    FFT(f,g,c,d,mid);
    c[0]=(2+MOD-c[0])%MOD;
    for (int i=1;i<d;i++) c[i]=(MOD-c[i])%MOD;
    for (int i = 0; i < mid; i++) c[d + i] = 0;
    FFT(c,g,g,d,mid);
    for (int i = 0; i < mid; i++) g[d + i] = 0;
}


void init() {
    n = 6e5;
    for (int i = fac[0] = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % MOD;
    inv[n] = power(fac[n], MOD - 2);
    for (int i = n; i > 0; i--)
        inv[i - 1] = inv[i] * i % MOD;
    n = 262144;
    for (int i = 0; i < n; i++)
        A[i] = i & 1 ? inv[2 * i + 1] : MOD - inv[2 * i + 1];
    for (int i = 0; i < n; i++)
        B[i] = A[i + 1] * (i + 1) % MOD;
    solve(A, C, n);
    memset(A, 0, sizeof(A));
    FFT(B, C, A, n, n);
    for (int i = 1; i < n; i++)
        B[i] = i & 1 ? A[i - 1] : MOD - A[i - 1];
    B[0] = 0;
}

LL get(int n)  {
    return B[n] * fac[2 * n - 1] % MOD;
}

int main() {
    init();
    for (scanf("%d", &T); T--;) {
        scanf("%d", &n);
        LL a = get(2 * n), b = get(n);
        printf("%lld\n", a * power(b, MOD - 3) % MOD);
    }
}

1009 Leading Robots

链接

题解

比较签到的题吧,就是有一些细节……首先有相同的可以直接 b a n ban 掉,然后考虑按加速度从小到大排序,记加速度为 a a ,位置为 p p ,如果存在 a i a j a_i \ge a_j p i p j p_i \ge p_j ,那么 j j 就可以扔掉了。

扔掉这些没用的之后,不难发现合法的位置都在以 a a x x p p y y 的上凸壳的顶点上。求一遍凸包即可。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> inline void chkmin(T &a, const T &b) { a = a < b ? a : b; }
template<typename T> inline void chkmax(T &a, const T &b) { a = a > b ? a : b; }

const int MAXN = 50005;
struct Po {
    int x, y;
    Po operator-(const Po &v) const {
        return Po { x - v.x, y - v.y };
    }
    LL operator*(const Po &v) const {
        return (LL)x * v.y - (LL)y * v.x;
    }
} po[MAXN], sta[MAXN], mx[MAXN];
bool ban[MAXN], bbn[MAXN];
bool cmp(const Po &a, const Po &b) {
    return a.x == b.x ? a.y < b.y : a.x < b.x;
}
bool equ(const Po &a, const Po &b) {
    return a.x == b.x && a.y == b.y;
}
int T, n;

int main() {
    for (scanf("%d", &T); T--;) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            scanf("%d%d", &po[i].y, &po[i].x);
        sort(po + 1, po + 1 + n, cmp);
        int cnt = 0;
        for (int i = 1; i <= n; i++)
            ban[i] = equ(po[i - 1], po[i]) || equ(po[i], po[i + 1]);
        for (int i = 1; i <= n; i++) {
            while (cnt > 0 && sta[cnt].y <= po[i].y) --cnt;
            sta[++cnt] = po[i];
            bbn[cnt] = ban[i];
        }
        for (int i = 1; i <= cnt; i++) po[i] = sta[i];
        n = cnt, cnt = 0;
        for (int i = 1; i <= n; i++) {
            while (cnt > 1 && (sta[cnt] - sta[cnt - 1]) * (po[i] - sta[cnt - 1]) >= 0) --cnt;
            sta[++cnt] = po[i], ban[cnt] = bbn[i];
        }
        int ans = 0;
        for (int i = 1; i <= cnt; i++) ans += !ban[i];
        printf("%d\n", ans);
    }
    return 0;
}

1010 Math is Simple

链接

吐槽

djq 现场爆推好像被卡常了,我赛后也拿莫反爆推了一遍仍然过不了(赛后的机子好像比赛中慢两倍左右)。

题解

题解还是相当妙的(如果 n = 1 0 7 n=10^7 暴力莫反什么的就随便过了……)

a + b n a+b \ge n 这个条件很奇怪,我们考虑 a + b = n a+b=n 的情况。

a = 1 , gcd ( a , n ) = 1 n / 2 1 1 a ( n a ) = 1 n a = 1 , gcd ( a , n ) = 1 n / 2 1 1 a + 1 n a = 1 n a = 1 , gcd ( a , n ) = 1 n 1 1 a \sum_{a=1,\gcd(a,n)=1}^{n/2-1}\frac{1}{a(n-a)} \\ = \frac{1}{n}\sum_{a=1,\gcd(a,n)=1}^{n/2-1}\frac{1}{a}+\frac{1}{n-a} \\ = \frac{1}{n}\sum_{a=1,\gcd(a,n)=1}^{n-1}\frac{1}{a}

注意 n = 2 n=2 时上式不成立(应当为 0 0 )!于是原题就变成了:

1 a < b n , gcd ( a , b ) = 1 n 1 a b 1 a < b < n , gcd ( a , b ) = 1 n 1 a b + 1 2 \sum_{1 \le a < b \le n,\gcd(a,b)=1}^n\frac{1}{ab}-\sum_{1 \le a < b < n,\gcd(a,b)=1}^n\frac{1}{ab}+\frac{1}{2}

加的 1 2 \frac{1}{2} 是因为 n = 2 n=2 时需要特判。于是发现大部分都消掉了,剩下的:

a = 1 , gcd ( a , n ) = 1 n 1 a n + 1 2 \sum_{a=1,\gcd(a,n)=1}^n\frac{1}{an}+\frac{1}{2}

仍然注意 n = 2 n=2 时前面那玩意儿不成立(应当为 0 0 ),总之 n = 2 n=2 的时候特判直接输出 1 2 \frac{1}{2} 就好了。

剩下的话前面直接套个莫反,就得到了:

1 n d n μ ( d ) 1 d a = 1 n / d 1 a + 1 2 \frac{1}{n}\sum_{d|n}\mu(d)\frac{1}{d}\sum_{a=1}^{n/d}\frac{1}{a}+\frac{1}{2}

倒数前缀和可以预处理,逆元可以预处理,所以每次查询是 O ( n u m b e r   o f   d i v s o r s ) O(number\ of\ divsors) 的。可以通过,常数很小。

总之第一步的想法很妙,给这题点赞。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
template<typename T> void chkmax(T &a, const T &b) { a = a < b ? b : a; }
template<typename T> void chkmin(T &a, const T &b) { a = a < b ? a : b; }

const int MAXN = 1e8 + 5, MAXM = 1e4 + 5, MOD = 998244353;
int inv[MAXN], prm[MAXM], vis[MAXM], pc, T, n;
vector<pair<int, int> > facs;
LL ans;
void init() {
    const int n = 1e8;
    inv[1] = 1;
    for (int i = 2; i <= n; i++)
        inv[i] = MOD - (LL)inv[MOD % i] * (MOD / i) % MOD;
    for (int i = 2; i <= n; i++)
        inv[i] = (inv[i - 1] + inv[i]) % MOD;
    const int m = 1e4;
    for (int i = 2; i <= m; i++) if (!vis[i]) {
        prm[++pc] = i;
        for (int j = i + i; j <= m; j += i) vis[j] = 1;
    }
}

void dfs(int p, int d, int u) {
    if (p == (int)facs.size()) {
        ans = (ans + (LL)u * (inv[d] - inv[d - 1]) * inv[n / d]) % MOD;
        return;
    }
    int c = facs[p].first, e = facs[p].second;
    for (int i = 0; i <= e && i < 2; i++, d *= c)
        dfs(p + 1, d, i == 0 ? u : -u);
}

int main() {
    init();
    for (scanf("%d", &T); T--;) {
        scanf("%d", &n);
        facs.clear();
        ans = 0;
        int nn = n;
        for (int i = 1; i <= pc && prm[i] * prm[i] <= nn; i++) {
            if (nn % prm[i]) continue;
            int c = 0;
            while (nn % prm[i] == 0) nn /= prm[i], ++c;
            facs.push_back(make_pair(prm[i], c));
        }
        if (nn > 1) facs.push_back(make_pair(nn, 1));
        dfs(0, 1, 1);
        ans = ans * (inv[n] - inv[n - 1]) % MOD;
        if (ans < 0) ans += MOD;
        if (n > 2) ans = (ans + inv[2] - 1) % MOD;
        printf("%lld\n", ans);
    }
    return 0;
}

1011 Minimum Index

链接

题解

刚开始我一直在想线性做法有点自闭……事实上非线性做法常数很小,可能不比线性差,最后用非线性做法过了……

和 JSOI2019 D3T3 很类似,结论稍微修一修就好了。

考虑维护前缀为 i i 时的最小后缀候选点集 S i S_{i} ,显然转移到 i + 1 i+1 时需要满足以下两个条件:

  1. S i + 1 S_{i+1} 中任意两个元素 a , b ( a < b ) a,b(a<b) 必然满足 s [ b . . . i + 1 ] s[b...i+1] s [ a . . . i + 1 ] s[a...i+1] 的前缀。否则已经比出大小必然可以删除一个。
  2. 考虑两个元素 a , b ( a < b ) a,b(a<b) ,必然满足 i a + 1 2 ( i b + 1 ) i-a+1 \ge 2(i-b+1) ,由于 s [ b . . . i + 1 ] s[b...i+1] s [ a . . . i + 1 ] s[a...i+1] b o r d e r border ,因此 s [ a . . . i + 1 ] s[a...i+1] b a b-a 的周期。如果上述条件不满足,那么 b b 必然不能再成为最小后缀(要么 a a 比他小,要么 2 b a 2b-a 比他小)。

于是 S S 中的元素任意时刻不会超过 O ( log n ) O(\log n) 个,复杂度 O ( n log n ) O(n \log n) ,跑得飞快。(代码长度基本都是快读板子部分……)

#include <bits/stdc++.h>
namespace IO {
    const int MAXR = 10000000;
    char _READ_[MAXR], _PRINT_[MAXR];
    int _READ_POS_, _PRINT_POS_, _READ_LEN_;
    inline char readc() {
    #ifndef ONLINE_JUDGE
        return getchar();
    #endif
        if (!_READ_POS_) {
            if (feof(stdin)) return -1;
            _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
        }
        char c = _READ_[_READ_POS_++];
        if (_READ_POS_ == _READ_LEN_) _READ_POS_ = 0;
        return c;
    }
    template<typename T> inline int read(T &x) {
        x = 0; register int flag = 1, c;
        while (((c = readc()) < '0' || c > '9') && c != '-')
            if (c < 0) return -1;
        if (c == '-') flag = -1; else x = c - '0';
        while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
        x *= flag; return 0;
    }
    inline int read(char *s) {
        register int len = 0, c;
        while (isspace(c = readc()) || c <= 0)
            if (c < 0) return -1;
        s[len++] = c;
        while (!isspace(c = readc()) && c) s[len++] = c;
        s[len] = 0;
        return len;
    }
    template<typename T1, typename ...T2> inline int read(T1 &a, T2&... x) {
        return read(a) | read(x...);
    }
    inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
    inline void printc(char c) {
        if (!c) return;
        _PRINT_[_PRINT_POS_++] = c;
        if (_PRINT_POS_ == MAXR) ioflush();
    }
    template<typename T> inline void print(T x, char c = '\n') {
        if (x < 0) printc('-'), x = -x;
        if (x) {
            static char sta[20];
            register int tp = 0;
            for (; x; x /= 10) sta[tp++] = x % 10 + '0';
            while (tp > 0) printc(sta[--tp]);
        } else printc('0');
        printc(c);
    }
    inline void print(char *s, char c = '\n') {
        for (int i = 0; s[i]; i++) printc(s[i]);
        printc(c);
    }
    inline void print(const char *s, char c = '\n') {
        for (int i = 0; s[i]; i++) printc(s[i]);
        printc(c);
    }
    template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
        print(x, ' '), print(y...);
    }
}
typedef long long LL;
using namespace std;

const int MAX_N = 1000005, MOD = 1e9 + 7;
vector<int> f, g;
char s[MAX_N];
int n, T;

int main() {
    for (IO::read(T); T--;) {
        n = IO::read(s);
    //    exkmp(s, n, z);
        f.clear(), g.clear();
        LL ans = 0, mul = 1;
        for (int i = 0, up = n - 1; i <= up; ++i) {
            g.clear();
            g.push_back(i);
            for (auto &x : f) {
                while (!g.empty() and s[x - g.back() + i] < s[i]) g.pop_back();
                if (g.empty() or s[x - g.back() + i] == s[i]) {
                    while (!g.empty() and i - x + 1 < ((i - g.back() + 1) << 1)) g.pop_back();
                    g.push_back(x);
                }
            }
            f.swap(g);
            ans = (ans + mul * (*f.begin() + 1)) % MOD;
            mul = mul * 1112 % MOD;
        }
        IO::print(ans);
    }
    IO::ioflush();
    return 0;
}

1012 Mow

链接

吐槽

比较可惜,被精度卡了……不然就 8 题队了。

题解

应该是签到题,可惜计算几何一向以恶心著称(精度问题能整死人)。

直接把多边形所有边往里平移 r r 形成一个新多边形,计算这个多边形的 + × r + π r 2 面积+周长 \times r+\pi r^2 就是割草机能够走到的面积。

直接半平面交算一波即可。复杂度 O ( n log n ) O(n \log n)

(代码还没写)

猜你喜欢

转载自blog.csdn.net/WAautomaton/article/details/107523935