bzoj刷题记录5.6-5.10

bzoj刷题记录5.6-5.10


bzoj3944: Sum

学习一个杜教筛。比较好的资料有:

http://blog.csdn.net/skywalkert/article/details/50500009

以及任之洲Orz的论文。

考虑

Sn=1inφ(i)

根据神奇的公式: d|nφ(d)=n 可以得到:

Sn=1in(id[d<i][i|d]φ(d))

化简并使用通用处理技术得到:

Sn=n(n+1)22pn1jnpφ(j)=n(n+1)22pnSnp

然后就可以下底分块了。可以证明 Tn=O(n3/4) 。如果线性筛预处理前 O(n2/3) 项则 Tn=O(n2/3)

μ 的处理同理,不过用到的是 d|nμ(d)=[n=1]

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 2500001;
int phi[MAXN], mu[MAXN], prime[MAXN], top = 0, not_prime[MAXN];
long long sphi[MAXN], smu[MAXN];
map<long long, long long> dp, d2;

void get_prime(int n)
{
    mu[1] = phi[1] = 1;
    for (register int i = 2; i <= n; i++) {
        if (!not_prime[i]) prime[++top] = i, mu[i] = -1, phi[i] = i-1;
        for (register int j = 1; j <= top && i*prime[j] <= n; j++) {
            not_prime[i*prime[j]] = 1;
            if (i%prime[j] == 0) {
                mu[i*prime[j]] = 0;
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            mu[i*prime[j]] = mu[i]*mu[prime[j]];
            phi[i*prime[j]] = phi[i]*phi[prime[j]];
        }
    }
    for (register int i = 1; i <= n; i++)
        sphi[i] = sphi[i-1]+phi[i], smu[i] = smu[i-1]+mu[i];//, dp[i] = sphi[i], d2[i] = smu[i];
}

struct query {
    long long x, y;
};

inline query sum_phi(long long n)
{
    if (n<MAXN) return (query){sphi[n], smu[n]};
    if (dp.count(n)) return (query){dp[n], d2[n]};
    register long long ans = n*(n+1)/2, a2 = 1;
    query q;
    for (register long long i = 2, last; i <= n; i = last+1) {
        last = n/(n/i);
        q = sum_phi(n/i);
        ans -= (last-i+1)*q.x;
        a2 -= (last-i+1)*q.y;
    }
    dp[n] = ans, d2[n] = a2;
    return (query){ans, a2};
}

int main()
{
    get_prime(MAXN-1);
    long long n;
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%lld", &n);
        query q = sum_phi(n);
        cout << q.x << " " << q.y << endl;
    }
    return 0;
}

虽然不是bzoj但同一主题也放上来吧

51nod1237: 1237 最大公约数之和 V3

比较有意思的题目。

injn(i,j)=1knk1ink1jnk[(i,j)=1]=kki,jd[d|i][d|j]μ(d)=1knkdμ(d)nkd2

后面的东西是不是很眼熟?还记得xx的gcd中推得的结论:

1in1jm[(i,j)=1]=dμ(d)ndmd

n=m ,则有:

21inφ(i)1=dμ(d)nd2

因此原式就是:

1knk21inkφ(i)1

然后就可以用杜教筛做 φ 前缀和,再对 nk 做下底函数分块即可。

#include <iostream>
#include <map>
#include <cstdio>
#include <cstring>
using namespace std;

const int MAXN = 2500001;
int prime[MAXN], top = 0, not_prime[MAXN], phi[MAXN], sphi[MAXN];
long long s[MAXN], n;
long long mod = 1000000007ll;
map<long long, long long> dp;

void get_prime(int n)
{
    phi[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (!not_prime[i]) prime[++top] = i, phi[i] = i-1;
        for (int j = 1; j <= top && prime[j]*i <= n; j++) {
            not_prime[i*prime[j]] = 1;
            if (i%prime[j] == 0) {
                phi[i*prime[j]] = phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]] = phi[i]*phi[prime[j]];
        }
    }
    for (int i = 1; i <= n; i++) sphi[i] = (sphi[i-1]+phi[i])%mod;
}

long long sum(long long n)
{
    if (n < MAXN) return sphi[n];
    if (dp.count(n)) return dp[n];
    long long ans = n%mod*(n%mod+1)/2%mod;
    for (register long long p = 2, last; p <= n; p = last+1) {
        last = n/(n/p);
        (ans -= sum(n/p)*(last-p+1)) %= mod;
    }
    return dp[n] = (ans+mod)%mod;
}

long long power(long long n)
{
    long long ans = 1, b = 2;
    for (int i = 0; i <= 63; i++) {
        if (n&(1ll<<i))
            (ans *= b) %= mod;
        (b *= b) %= mod;
    }
    return ans;
}

int main()
{
    scanf("%lld", &n);
    get_prime(MAXN-1);
    long long ans = 0;
    for (register long long k = 1, last; k <= n; k = last+1) {
        last = n/(n/k);
        (ans += (k+last)%mod*(last-k+1)%mod*500000004ll%mod*(2*sum(n/k)-1)%mod) %= mod;
    }
    cout << ans << endl;
    return 0;
}

bzoj1044: [HAOI2008]木棍分割

水题..然而神奇的是sdp那个数组如果取模速度会慢4倍左右而被肉眼可见卡常数…

哪位松爷的忠实粉丝来解释下..

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 50005;
int l[MAXN], n, m;
long long dp[MAXN][2], s[MAXN], sdp[MAXN];
int pos[MAXN];

inline int read()
{
    int a = 0, c;
    do c = getchar(); while(!isdigit(c));
    while (isdigit(c)) {
        a = a*10+c-'0';
        c = getchar();
    }
    return a;
}

bool judge(int k)
{
    int tm = m, len = 0;
    for (register int i = 1; i <= n; i++) {
        if (len+l[i] > k) len = l[i], tm--;
        else len = len+l[i];
        if (len > k) return 0;
        if (tm < 0) return 0;
    }
    return 1;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        l[i] = read(), s[i] = s[i-1]+l[i];
    }
    int l = 0, r = 50000000, mid, ans;
    while (l <= r) {
        mid = (l+r)>>1;
        if (judge(mid)) r = mid-1;
        else l = mid+1;
    }
    ans = r+1;
    for (register int i = 0; i <= n; i++)
        dp[i][0] = (s[i] <= ans);
    int now = 1, pre = 0;
    int cnt = (s[n] <= ans);
    register int k = 1;
    for (register int i = 0; i <= n; i++) {
        while (s[i]-s[k] > ans) k++;
        pos[i] = k;
    }
    for (int j = 1; j <= m; j++) {
        for (register int k = 0; k <= n; k++) sdp[k+1] = sdp[k]+dp[k][pre];
        for (register int i = 0; i <= n; i++)
            dp[i][now] = (sdp[i]-sdp[pos[i]])%10007;
        (cnt += dp[n][now]) %= 10007;
        swap(now, pre);
    }
    cout << ans << " " << (cnt+10007)%10007 << endl;
    return 0;
}

bzoj1923: [Sdoi2010]外星千足虫

被输入整惨了…
为什么bitset的输入逻辑这么诡异…

#include <bits/stdc++.h>
using namespace std;

bitset<1005> g[2005], y, x;
int n, m, b;
int lab[2005];

int readbit()
{
    int c = 0;
    do c = getchar(); while (!isdigit(c));
    return c-'0';
}

void gauss()
{
    int k = 0;
    for (int i = 0; i < m; i++) {
        if (g[i][i] == 0) {
            for (register int j = i+1; j < m; j++)
                if (g[j][i]) {
                    swap(g[i], g[j]), swap(lab[i], lab[j]);
                    int t = y[i]; y[i] = y[j], y[j] = t;
                    break;
                }
            if (g[i][i] == 0) continue;
        }
        k = max(k, lab[i]);
        for (register int j = 0; j < m; j++)
            if (j != i && g[j][i]) {
                g[j] = (g[j]^g[i]);
                y[j] = (y[j]^y[i]);
            }
    }
    for (int i = n-1; i >= 0; i--)
        if (!g[i][i]) {
            puts("Cannot Determine");
            return;
        }
    printf("%d\n", k+1);
    for (int i = 0; i < n; i++)
        if (y[i]) puts("?y7M#");
        else puts("Earth");
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        lab[i] = i;
        for (register int j = 0; j < n; j++) g[i][j] = readbit();
        y[i] = readbit();
    }
    gauss();
    return 0;
}

bzoj4516: [Sdoi2016]生成魔咒

SAM秒掉了..小清新数据结构题..
这才是省选该有的画风吧QwQ

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
map<int, int> chl[MAXN*2];
int fa[MAXN*2], maxl[MAXN*2];
int top = 1, root = 1, last = 1;
long long ans = 0;
int n, x;

void push(int x)
{
    int p = last, np = ++top; maxl[np] = maxl[p]+1;
    while (p && !chl[p][x]) chl[p][x] = np, p = fa[p];
    if (!p) fa[np] = root, ans += maxl[np]-maxl[fa[np]];
    else {
        int q = chl[p][x];
        if (maxl[q] == maxl[p]+1) fa[np] = q, ans += maxl[np]-maxl[fa[np]];
        else {
            int nq = ++top; maxl[nq] = maxl[p]+1;
            chl[nq] = chl[q];
            ans -= maxl[q]-maxl[fa[q]];
            fa[nq] = fa[q], fa[q] = nq, fa[np] = nq;
            ans += maxl[nq]-maxl[fa[nq]], ans += maxl[q]-maxl[fa[q]], ans += maxl[np]-maxl[fa[np]];
            while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p];
        }
    }
    last = np;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x), push(x);
        printf("%d\n", ans);
    }
    return 0;
}

bzoj2431: [HAOI2009]逆序对数列

刷水有益健康。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1005, MAXK = 1005;
int n, k;
int dp[2][1005], s[1005];

int main()
{
    scanf("%d%d", &n, &k);
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (register int j = 0; j <= k; j++) s[j+1] = (s[j]+dp[0][j])%10000;
        for (register int j = 0; j <= k; j++) {
            int l = max(0, j-(i-1)), r = j;
            dp[1][j] = (s[r+1]-s[l])%10000;
        }
        swap(dp[0], dp[1]);
    }
    cout << (dp[0][k]+10000)%10000 << endl;
    return 0;
}

bzoj4755: [Jsoi2016]扭动的回文串

核心是第三个操作的重要结论:A、B两串扭曲成回文串时,若回文中心在A中,则该串必然包含以其为中心最长的回文子串。

证明:回文中心是缝隙的通过添加无用字符可以转化为回文中心是字符,设回文中心为 i ,扭曲回文串半长为l,则在 A 中的部分为S1=A[il,i+k],在 B 中的位置为S2=B[i+k,i+l1],如果存在一个 p>k 使得 S3=A[ip,i+p] 为回文串,则只需证明 S=A[il,i+p]B[i+p,i+l1] 构成一回文串即可说明这种策略不劣于其他策略。

设:

T=A[il,ip+1],TS1S2P=B[i+p,i+l1],PS1S2P1S12S11

由于 S1S2 为回文串,有 S1S2=(S1S2)1=S12S11 ,所以 T,P1S1S2 ,又 |T|=|P| ,所以 T=P1,T1=P 。由于 S3 是回文串,有 S3=S13 。因此

S=TS3PS1=(TS3P)1=P1S13T1=TS3P=S

所以 S 构成一回文串,证毕。

另一方面还需要证明如果一个回文串被方案真包含那么它一定不是最长回文串,由于太显然就不证了。

其实画个图就都显然了啊233

之后这题就是代码题了,需要细致讨论每一种情况。由于懒各种复制,代码冗长不可读,1A真是侥幸…

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
typedef unsigned long long ull;

char A[MAXN], B[MAXN];
ull hashA[MAXN], hashB[MAXN], invA[MAXN], invB[MAXN], power[MAXN];
int bas = 31, n;
int pA[MAXN][2], pB[MAXN][2];

inline unsigned long long hash_val(ull hst[], int l, int r)
{ return hst[r]-power[r-l+1]*hst[l-1]; }
inline unsigned long long hash_inv(ull hst[], int l, int r)
{ return hst[l]-power[r+1-l]*hst[r+1]; }

int maxlen(ull str[], ull inv[])
{
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = min(i-1, n-i), mid;
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(str, i-mid, i) == hash_inv(inv, i, i+mid)) l = mid+1;
            else r = mid-1;
        }
        ans = max(ans, 2*(l-1)+1);
    }
    for (int i = 1; i < n; i++) {
        int l = 1, r = min(i, n-i), mid;
        if (hash_val(str, i, i) != hash_val(str, i+1, i+1)) continue;
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(str, i-mid+1, i) == hash_inv(inv, i+1, i+mid)) l = mid+1;
            else r = mid-1;
        }
        ans = max(ans, 2*(l-1));
    }
    return ans;
}

int maxl(ull iA[], int i, ull B[], int j)
{
    int l = 1, r = min(i, n-j+1), mid;
    if (hash_inv(iA, i, i) != hash_val(B, j, j)) return 0;
    while (l <= r) {
        mid = (l+r)>>1;
        if (hash_inv(iA, i-mid+1, i) == hash_val(B, j, j+mid-1)) l = mid+1;
        else r = mid-1;
    }
    return l-1;
}

int maxdouble(ull A[], ull iA[], ull B[])
{
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = min(i-1, n-i), mid;
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(A, i-mid, i) == hash_inv(iA, i, i+mid)) l = mid+1;
            else r = mid-1;
        }
        int len = l-1;
        ans = max(ans, 2*len+1+2*maxl(iA, i-len-1, B, i+len));
    }
    for (int i = 1; i <= n; i++) {
        int l = 1, r = min(i, n-i), mid;
        if (hash_val(A, i, i) != hash_val(A, i+1, i+1)) {
            ans = max(ans, 2*maxl(iA, i, B, i));
            continue;
        }
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(A, i-mid+1, i) == hash_inv(iA, i+1, i+mid)) l = mid+1;
            else r = mid-1;
        }
        int len = l-1;
        ans = max(ans, 2*len+2*maxl(iA, i-len, B, i+len));
    }
    return ans;
}

int maxdouble2(ull A[], ull iA[], ull iB[])
{
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = min(i-1, n-i), mid;
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(A, i-mid, i) == hash_inv(iA, i, i+mid)) l = mid+1;
            else r = mid-1;
        }
        int len = l-1; //todo
        ans = max(ans, 2*len+1+2*maxl(iB, i-len, A, i+len+1));
    } 
    for (int i = 1; i <= n; i++) {
        int l = 1, r = min(i, n-i), mid;
        if (hash_val(A, i, i) != hash_val(A, i+1, i+1)) {
            ans = max(ans, 2*maxl(iB, i, A, i));
            continue;
        }
        while (l <= r) {
            mid = (l+r)>>1;
            if (hash_val(A, i-mid+1, i) == hash_inv(iA, i+1, i+mid)) l = mid+1;
            else r = mid-1;
        }
        int len = l-1;
        ans = max(ans, 2*len+2*maxl(iB, i-len+1, A, i+len+1));
    }
    return ans;
}

int main()
{
    scanf("%d", &n);
    scanf("%s", A+1);
    scanf("%s", B+1);
    power[0] = 1;
    for (int i = 1; i <= n; i++) power[i] = power[i-1]*bas;
    for (int i = 1; i <= n; i++) {
        hashA[i] = hashA[i-1]*bas+A[i]-'A'+1;
        hashB[i] = hashB[i-1]*bas+B[i]-'A'+1;
    }
    for (int i = n; i >= 1; i--) {
        invA[i] = invA[i+1]*bas+A[i]-'A'+1;
        invB[i] = invB[i+1]*bas+B[i]-'A'+1;
    }
    int ans = max(maxlen(hashA, invA), maxlen(hashB, invB));
    ans = max(ans, maxdouble(hashA, invA, hashB));
    ans = max(ans, maxdouble2(hashB, invB, invA));
    cout << ans << endl;
    return 0;
}

bzoj3876: [Ahoi2014]支线剧情

上下界费用流建模还是比较显然的..
然而会被卡常数..于是抄了zkw费用流过了..
有时间研究一下。

spfa费用流(TLE):

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 305, MAXM = 100005, inf = 233333333;

struct node {
    int to, next, flow, cost, neg;
} edge[MAXM];
int head[MAXN], top = 0;
inline void push(int i, int j, int f, int c)
{
    ++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
    ++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top;
}

const int S = 301, T = 302, SS = 303, ST = 304;
int addin = 0;

inline void push_lim(int i, int j, int cost, int minc)
{
    push(SS, j, minc, cost), push(i, ST, minc, 0);
    addin += minc;
    push(i, j, inf, cost);
}

queue<int> que;
int dis[MAXN], vis[MAXN], pre[MAXN], pre_edge[MAXN];

bool spfa(int &cost, int &maxf)
{
    memset(dis, 127/3, sizeof dis), memset(vis, 0, sizeof vis);
    vis[SS] = 1, dis[SS] = 0, que.push(SS);
    while (!que.empty()) {
        int nd = que.front(); vis[nd] = 0, que.pop();
        for (register int i = head[nd]; i; i = edge[i].next) {
            int to = edge[i].to, f = edge[i].flow, c = edge[i].cost;
            if (!f) continue;
            if (dis[to] > dis[nd]+c) {
                dis[to] = dis[nd]+c;
                pre[to] = nd, pre_edge[to] = i;
                if (!vis[to]) vis[to] = 1, que.push(to);
            }
        }
    }
    if (dis[ST] >= inf) return 0;
    int mx = inf;
    for (register int i = ST; i != SS; i = pre[i]) mx = min(mx, edge[pre_edge[i]].flow);
    for (register int i = ST; i != SS; i = pre[i])
        edge[pre_edge[i]].flow -= mx, edge[edge[pre_edge[i]].neg].flow += mx;
    maxf += mx, cost += mx*dis[ST];
    // cout <<endl<< mx << " " << mx*dis[ST] << "--\n";
    return 1;
}

int mcf(int &cost)
{
    cost = 0;
    int ans = 0;
    while (spfa(cost, ans));
    return ans;
}

int n, k, u, t;

int main()
{
    scanf("%d", &n);
    push(T, S, inf, 0), push(S, 1, inf, 0);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &k);
        push(i, T, inf, 0);
        for (register int j = 1; j <= k; j++) {
            scanf("%d%d", &u, &t);
            push_lim(i, u, t, 1);
        }
    }
    int f = 0, c = 0;
    f = mcf(c);
    cout << c << endl;
    return 0;
}

zkw费用流(AC):

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 305, MAXM = 100005, inf = 233333333;

struct node {
    int to, next, flow, cost, neg;
} edge[MAXM];
int head[MAXN], top = 0;
inline void push(int i, int j, int f, int c)
{
    ++top, edge[top] = (node){j, head[i], f, c, top+1}, head[i] = top;
    ++top, edge[top] = (node){i, head[j], 0, -c, top-1}, head[j] = top;
}

const int S = 301, T = 302, SS = 303, ST = 304;
int addin = 0;

inline void push_lim(int i, int j, int cost, int minc)
{
    push(SS, j, minc, cost), push(i, ST, minc, 0);
    addin += minc;
    push(i, j, inf, cost);
}

bool vis[MAXN];
int cost = 0, pi1 = 0;

int arg(int nd, int maxf = inf)
{
    if (nd == ST) return cost += maxf*pi1, maxf;
    vis[nd] = 1;
    // cout << nd << endl;
    int ans = 0, t;
    for (int i = head[nd]; i; i = edge[i].next) {
        int to = edge[i].to, c = edge[i].cost, f = edge[i].flow;
        if (!f || vis[to] || c) continue;
        t = arg(to, min(maxf, f));
        ans += t, maxf -= t, edge[i].flow -= t, edge[edge[i].neg].flow += t;
        if (!maxf) break;
    }
    return ans;
}


bool modlabel()
{
    int d = inf;
    for (int i = 1; i <= MAXN-1; i++) if (vis[i])
        for (int j = head[i]; j; j = edge[j].next)
            if (edge[j].flow && !vis[edge[j].to])
                d = min(d, edge[j].cost);
    if (d == inf) return 0;
    for (int i = 1; i <= MAXN-1; i++) if (vis[i])
        for (int j = head[i]; j; j = edge[j].next)
            edge[j].cost -= d, edge[edge[j].neg].cost += d;
    pi1 += d;
    return 1;
}

int n, k, u, t;

void mcf()
{
    do do
    memset(vis, 0, sizeof vis);
    while (arg(SS));
    while (modlabel());
}


int main()
{
    scanf("%d", &n);
    push(T, S, inf, 0), push(S, 1, inf, 0);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &k);
        push(i, T, inf, 0);
        for (register int j = 1; j <= k; j++) {
            scanf("%d%d", &u, &t);
            push_lim(i, u, t, 1);
        }
    }
    mcf();
    cout << cost << endl;
    return 0;
}

bzoj2762: [JLOI2011]不等式组

分类讨论题..

#include <bits/stdc++.h>
using namespace std;

int lp = -2e6, rp = 2e6;
void calc(int a, int b, int c, int &l, int &r)
{
    c = c-b;
    // cout << a << " " << b << " " << c << endl;
    if (a > 0) {
        if (c > 0) l = c/a+1, r = rp;
        else if (c < 0) l = c/a+(c%a==0), r = rp;
        else l = 1, r = rp;
    } else if (a < 0) {
        if (c > 0) l = lp, r = c/a-1;
        else if (c < 0) l = lp, r = c/a-(c%a==0);
        else l = lp, r = -1;
    } else {
        if (c < 0) l = lp, r = rp;
        else l = 1, r = 0;
    }
    if (l < lp) l = lp;
    if (r > rp) r = rp;
}

int c[4000010], n = 4e6+2;
inline int lowbit(int i) { return i&(-i); }
int sum(int i)
{
    i += 2e6+1;
    int ans = 0;
    while (i) ans += c[i], i -= lowbit(i);
    return ans;
}
void add(int pos, int dt)
{ for (int i = pos; i <= n; i += lowbit(i)) c[i] += dt; }

void modify(int l, int r, int dt)
{
    // printf("[%d,%d] -- %d\n", l, r, dt);
    if (l > r) return;
    l += 2e6+1, r += 2e6+1;
    add(l, dt), add(r+1, -dt);
}

int a[100005], b[100005], d[100005], dd[100005], top = 0, n2;
char str[10];

int main()
{
    scanf("%d", &n2);
    for (int i = 1; i <= n2; i++) {
        scanf("%s", str);
        if (str[0] == 'A') {
            ++top, scanf("%d%d%d", &a[top], &b[top], &d[top]);
            int l, r;
            calc(a[top], b[top], d[top], l, r);
            modify(l, r, 1);
        } else if (str[0] == 'D') {
            int u;
            scanf("%d", &u);
            if (dd[u] == 1) continue;
            dd[u] = 1;
            int l, r; calc(a[u], b[u], d[u], l, r);
            modify(l, r, -1);
        } else {
            int k; scanf("%d", &k);
            printf("%d\n", sum(k));
        }
    }
    return 0;
}

bzoj3879: SvT

貌似也是套路题..建好SA后把相隔两点间区间最值扔进单调栈.
模块调试大法好.

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 500010;
struct SA {
    struct ele {
        int k[2];
        int id;
        ele(){k[0] = k[1] = 0; }
        ele(int i, int x, int y) {id = i, k[0] = x, k[1] = y; }
    } RE[MAXN], RT[MAXN];
    int sa[MAXN], rank[MAXN], height[MAXN], sum[MAXN], A[MAXN], n;
    void radix_sort()
    {
        for (int y = 1; y >= 0; y--) {
            memset(sum, 0, sizeof sum);
            for (int i = 1; i <= n; i++) sum[RE[i].k[y]]++;
            for (int i = 1; i < MAXN; i++) sum[i] += sum[i-1];
            for (int i = n; i >= 1; i--) RT[sum[RE[i].k[y]]--] = RE[i];
            for (int i = 1; i <= n; i++) RE[i] = RT[i];
        }
        for (int i = 1; i <= n; i++) {
            rank[RE[i].id] = rank[RE[i-1].id];
            if (RE[i].k[0] != RE[i-1].k[0] || RE[i].k[1] != RE[i-1].k[1])
                rank[RE[i].id]++;
        }
    }
    void calc_sa()
    {
        for (int i = 1; i <= n; i++) RE[i] = ele(i, A[i], 0);
        radix_sort();
        for (int k = 1; k < n; k <<= 1) {
            for (int j = 1; j <= n; j++) RE[j] = ele(j, rank[j], j+k<=n?rank[j+k]:0);
            radix_sort();
        }
        for (int i = 1; i <= n; i++) sa[rank[i]] = i;
    }
    void calc_height()
    {
        int h = 0;
        for (int i = 1; i <= n; i++) {
            if (rank[i] == 1) h = 0;
            else {
                int k = sa[rank[i]-1];
                if (--h < 0) h = 0;
                while (A[k+h] == A[i+h]) h++;
            }
            height[rank[i]] = h;
        }
    }
} SA;

int m;

inline int read() {
    int a = 0, c;
    do c = getchar(); while(!isdigit(c));
    while (isdigit(c)) {
        a = a*10+c-'0';
        c = getchar();
    }
    return a;
}

int pos[MAXN];
int f[MAXN][21];

inline int min_val(int l, int r)
{
    if (l > r) return 0;
    int k = int(log2(r-l+1)+0.001);
    return min(f[l][k], f[r-(1<<k)+1][k]);
}

const long long mod = 23333333333333333ll;

struct slv {
    struct ele {
        long long dat, len;
    } stk[MAXN];
    int top;
    long long ans, sum;
    void clear()
    { top = ans = sum = 0; }
    void push(long long x)
    {
        if (!top) { stk[++top] = (ele){x, 1}, (sum += x) %= mod, (ans += sum) %= mod; return; }
        ele k = (ele){x, 1};
        while (stk[top].dat > x)
            k.len += stk[top].len, (sum -= stk[top].dat*stk[top].len) %= mod, top--;
        stk[++top] = k, (sum += stk[top].dat*stk[top].len) %= mod, (ans += sum) %= mod;
    }
} solver;

int main()
{
    scanf("%d%d\n", &SA.n, &m);
    for (int i = 1; i <= SA.n; i++)
        scanf("%c", &SA.A[i]), SA.A[i] -= 'a'-1;
    SA.calc_sa();
    SA.calc_height();
    for (int i = 1; i <= SA.n; i++) f[i][0] = SA.height[i];
    for (int j = 1; j <= 20; j++)
        for (int i = 1; i <= SA.n; i++) {
            f[i][j] = f[i][j-1];
            if (i+(1<<(j-1)) <= SA.n) f[i][j] = min(f[i][j], f[i+(1<<(j-1))][j-1]);
        }
    for (int i = 1; i <= m; i++) {
        int t; t = read();
        solver.clear();
        for (int j = 1; j <= t; j++)
            pos[j] = SA.rank[read()];
        sort(pos+1, pos+t+1);
        t = unique(pos+1, pos+t+1)-pos-1;
        for (int j = 1; j < t; j++) pos[j] = min_val(pos[j]+1, pos[j+1]);
        for (int j = 1; j < t; j++) solver.push(pos[j]);
        printf("%lld\n", solver.ans);
    } 
    return 0;
}

bzoj4753: [Jsoi2016]最佳团体

分数规划.先二分答案,然后用树形dp判断。
这个dp比较神奇,是按照dfs序dp的。
然而被卡精度…

WA:

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 3005;
struct node {
    int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }

int n, k;
int s[MAXN], p[MAXN], r[MAXN];
int dfn[MAXN], dfn_top = 0, siz[MAXN];
long double dp[MAXN][MAXN], dat[MAXN];

void dfs(int nd)
{
    siz[nd] = 1, dfn[++dfn_top] = nd;
    for (int i = head[nd]; i; i = edge[i].next)
        dfs(edge[i].to), siz[nd] += siz[edge[i].to];
}

long double do_dp()
{
    for (int i = 0; i <= n+2; i++)
        for (int j = 0; j <= k+1; j++)
            dp[i][j] = -1e20;
    dp[1][0] = 0;
    for (int i = 1; i <= n+1; i++) {
        for (int j = 0; j <= min(k+1, i-1); j++) {
            if (dp[i][j] == -1e20) continue;
            dp[i+1][j+1] = max(dp[i+1][j+1], dp[i][j] + dat[dfn[i]]);
            dp[i+siz[dfn[i]]][j] = max(dp[i+siz[dfn[i]]][j], dp[i][j]);
            // cout << dp[i][j] << " ";
        }
        // puts("");
    }
    return dp[n+2][k+1];
}

const long double eps = 1e-5;

bool judge(long double md)
{
    for (int i = 1; i <= n; i++)
        dat[i] = p[i]-s[i]*md;
    do_dp();
    return dp[n+2][k+1] >= 0.0;
}

int main()
{
    scanf("%d%d", &k, &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d%d", &s[i], &p[i], &r[i]);
    for (int i = 1; i <= n; i++)
        push(r[i]?r[i]:n+1, i);
    dfs(n+1);
    // for (int i = 1; i <= n+1; i++)
    //     cout << dfn[i] << " ";
    // puts("");
    long double l = 0, r = 1e4, mid;
    while (r-l >= eps) {
        mid = (l+r)/2;
        // cout << mid << endl;
        if (judge(mid)) l = mid;
        else r = mid;
    }
    printf("%.3lf\n", mid);
    return 0;
}

bzoj4827: [Hnoi2017]礼物

首先用一眼法知道c是单峰的。
然后就三分+FFT了..擦边AC..

【233ms的神犇是怎么做的??】

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 400005;
typedef complex<double> Complex;

int rev[MAXN];
Complex x[MAXN], y[MAXN], z[MAXN], A[MAXN];
const double PI = acos(-1);
void FFT(Complex a[], int n, int flag)
{
    rev[0] = 0;
    int lgn = int(log2(n)+0.001);
    for (int i = 1; i < n; i++) rev[i] = (rev[i>>1]>>1)|((i&1)<<(lgn-1));
    for (int i = 0; i < n; i++) A[rev[i]] = a[i];
    for (int k = 2; k <= n; k <<= 1) {
        Complex dw = Complex(cos(2*PI/k), flag*sin(2*PI/k)), u, v;
        for (register int i = 0; i < n; i += k) {
            Complex w = Complex(1, 0);
            for (register int j = 0; j < k>>1; j++) {
                u = A[i+j], v = w*A[i+j+(k>>1)];
                A[i+j] = u+v, A[i+j+(k>>1)] = u-v;
                w *= dw;
            }
        }
    }
    for (int i = 0; i < n; i++)
        if (flag == 1) a[i] = A[i];
        else a[i] = A[i]/Complex(n, 0);
}

int n, m;
int xi[MAXN], yi[MAXN];
long long zi[MAXN];

long long ans = INT_MAX, cnt = 0;
int w = 1;

long long solve(int i)
{
    cnt = 0;
    for (int j = 0; j < n; j++) x[j] = Complex(xi[j]+i, 0), y[n-j-1] = Complex(yi[j], 0), cnt += (xi[j]+i)*(xi[j]+i)+yi[j]*yi[j];
    for (int j = n; j < w; j++) x[j] = y[j] = Complex(0, 0);
    FFT(x, w, 1), FFT(y, w, 1);
    for (int j = 0; j < w; j++) z[j] = x[j]*y[j];
    FFT(z, w, -1), FFT(x, w, -1);
    for (int j = 0; j < w; j++) zi[j] = (long long)(z[j].real()+0.001);
    long long d = INT_MAX;
    for (int j = 0; j < n; j++)
        d = min(d, cnt-2*(zi[j]+zi[n+j]));
    return d;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) scanf("%d", &xi[i]);
    for (int i = 0; i < n; i++) scanf("%d", &yi[i]);
    for (; w <= 2*n; w <<= 1);
    int a = -m, d = m, b, c;
    while (d-a >= 3) {
        b = a+(d-a)/3, c = a+(d-a)/3*2;
        if (solve(b) < solve(c))  d = c;
        else a = b;
    }
    for (int i = a; i <= d; i++)
        ans = min(ans, solve(i));
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值