[BZOJ 4513 ~ 4518] SDOI 2016 R1 day1 + day2 口胡题解

题面自己去看吧哈哈哈哈哈哈哈哈哈
这周一开始前前后后花了三天写完….还犯了一个又一个低级错误……我是思博呢

Day 1 :

T1,BZOJ 4513 储能表
简单可做的数位dp, 记录 f[ i ][0 / 1][0 / 1][0/ 1] 表示考虑到二进制第 i 位 (从高到低位) ,是否卡住 n 、 是否卡住 m 、是否把 k 减完的方案数和此时的和,随便转移转移就好辣。

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;

int mo;
LL n, m, K;

struct Data
{
    int v, s;

    Data() {}
    Data(int v, int s) : v(v), s(s) {}

    inline Data& operator += (const Data & b) 
    {
        (v += b.v) >= mo ? v -= mo : 0;
        (s += b.s) >= mo ? s -= mo : 0;
        return *this;
    }
    inline Data operator + (LL x)
    {
        return Data((x % mo * s + v) % mo, s);
    }
};

Data f[2][2][2][2];

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    int T;
    scanf("%d", &T);

    while (T--)
    {
        scanf("%lld%lld%lld%d", &n, &m, &K, &mo);
        --n, --m;

        memset(f[0], 0, sizeof(f[0]));
        f[0][0][0][0] = Data(0, 1);

        int t = 0;

        for (int i = 59; ~i; --i) 
        {
            memset(f[t ^ 1], 0, sizeof(f[t ^ 1]));

            bool ok = (K >> i) & 1;
            if (ok) K ^= (1ll << i);

            for (int j1 = 0; j1 < 2; ++j1) for (int j2 = 0; j2 < 2; ++j2) 
            {
                bool f1 = j1 || (n & (1ll << i)), f2 = j2 || (m & (1ll << i));
                Data x = f[t][j1][j2][0], y = f[t][j1][j2][1];

                if (ok)
                {
                    if (f1) 
                    {
                        f[t ^ 1][j1][f2][0] += x;
                        f[t ^ 1][j1][f2][1] += y + (1ll << i);
                    }
                    if (f2)
                    {
                        f[t ^ 1][f1][j2][0] += x;
                        f[t ^ 1][f1][j2][1] += y + (1ll << i);
                    }
                    f[t ^ 1][f1][f2][1] += y;
                    if (f1 && f2)
                        f[t ^ 1][j1][j2][1] += y;
                }
                else
                {
                    if (f1)
                    {
                        f[t ^ 1][j1][f2][1] += x + ((1ll << i) - K);
                        f[t ^ 1][j1][f2][1] += y + (1ll << i);
                    }
                    if (f2)
                    {
                        f[t ^ 1][f1][j2][1] += x + ((1ll << i) - K);
                        f[t ^ 1][f1][j2][1] += y + (1ll << i);
                    }
                    f[t ^ 1][f1][f2][0] += x;
                    f[t ^ 1][f1][f2][1] += y;
                    if (f1 && f2)
                    {
                        f[t ^ 1][j1][j2][0] += x;
                        f[t ^ 1][j1][j2][1] += y;
                    }
                }
            }
            t ^= 1;
        }

        int ret = 0;
        for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) for (int k = 0; k < 2; ++k)
            (ret += f[t][i][j][k].v) >= mo ? ret -= mo : 0;

        printf("%d\n", ret);
    }

    return 0;
}

T2, BZOJ 4514

给我贡献了一大波WA的题…题解就是注意到二分图性质跑最大流就可以辣


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;
inline void read(int &x)
{
    char c; bool f = 0;
    while ((c = getchar()) < '0' || c > '9') if (c == '-') f = 1;
    for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0');
    if (f) x = -x;
}

const LL inf = 1e15;
const int N = 211;

struct edge
{
    int to, n;
    LL c, w;
} e[N * N];
int head[N], tot = 1;

inline void addEdge(int x, int y, LL c, LL w)
{
    e[++tot] = (edge) {y, head[x], c, w}, head[x] = tot;
    e[++tot] = (edge) {x, head[y], 0, -w}, head[y] = tot;
}

LL d[N];
int pa[N], S, T;

bool spfa()
{
    static bool inq[N];
    static int que[N], l, r;

    for (int i = S + 1; i <= T; ++i) d[i] = -inf;

    l = r = 0, que[++r] = S;

    while (l != r)
    {
        int u = que[++l];
        l == 205 ? l = 0 : 0, inq[u] = 0;
        for (int p = head[u], to; p; p = e[p].n) if (e[p].c)
        {
            to = e[p].to;
            if (d[to] < d[u] + e[p].w) 
            {
                d[to] = d[u] + e[p].w, pa[to] = p;
                if (!inq[to])
                {
                    que[++r] = to, inq[to] = 1;
                    r == 205 ? r = 0 : 0;
                }
            }
        }
    }
    return d[T] > -inf;
}

void augment(LL & a, LL & b)
{
    int x = T; LL ret = inf;
    while (x != S)
    {
        ret = min(ret, e[pa[x]].c);
        x = e[pa[x] ^ 1].to;
    }
    x = T;
    while (x != S)
    {
        e[pa[x]].c -= ret, e[pa[x] ^ 1].c += ret;
        x = e[pa[x] ^ 1].to;
    }
    a = d[T], b = ret;
}

int calc(int x)
{
    int ret = 0;
    for (int i = 2, m = sqrt(x); i <= m; ++i) 
        while (x % i == 0) ++ret, x /= i;
    if (x > 1) ++ret;
    return ret;
}
bool is_prime(int x)
{
    for (int i = 2, m = sqrt(x); i <= m; ++i) 
        if (x % i == 0) return 0;
    return 1;
}

bool L[N];
int a[N], b[N], c[N], n;

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    read(n);
    for (int i = 1; i <= n; ++i) read(a[i]);
    for (int i = 1; i <= n; ++i) read(b[i]);
    for (int i = 1; i <= n; ++i) read(c[i]);

    for (int i = 1; i <= n; ++i) L[i] = calc(a[i]) & 1;

    S = 0, T = n + 1;
    for (int i = 1; i <= n; ++i)
    {
        L[i] ? addEdge(S, i, b[i], 0) : addEdge(i, T, b[i], 0);
        for (int j = i - 1, x, y; j; --j) if (L[i] ^ L[j])
        {
            x = a[i], y = a[j];
            if (x < y) swap(x, y);
            if (x % y == 0 && is_prime(x / y))
                L[i] ? addEdge(i, j, inf, (LL) c[i] * c[j]) : addEdge(j, i, inf, (LL) c[i] * c[j]);
        }
    }

    LL ret = 0, flow = 0, a, b;
    while (spfa())
    {
        augment(a, b);
        if (ret + a * b < 0)
        {
            flow += ret / -a; break ;
        }
        ret += a * b, flow += b;
    }

    printf("%lld\n", flow);

    return 0;
}

T3, BZOJ 4515

以前没有接触过的新姿势… 他们叫它李超线段树…
首先强行上树的题目自然是要链剖爬一爬树啦,接着,用线段树维护已经插入的线段及其最小值,插入一个线段时和当前线段树里保存的线段比较一下,算算两个的斜率和“优势区间“, 把优势区间比较小的线段暴力下放。

Q : 优势区间是什么?
A : 注意到两个线段如果不相交的话自然有一个线段完全无贡献,否则算出两个线段的交点,然后把一个处于下方的长度较短的线段下放下去,不会影响答案。

每次 modify 时候最多更新 log 个线段,线段下放最多 log 层, 故复杂度是 O(n log ^ 2 n) 的,可不是什么玄学做法!

因为第一次写,无耻的要数据调啊调了半个下午 QAQ


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
inline void read(int &x)
{
    char c; bool f = 0;
    while ((c = getchar()) < '0' || c > '9') if (c == '-') f = 1;
    for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0');
    if (f) x = -x;
}

const LL inf = 123456789123456789;
const int N = 1e5 + 10 ;

struct edge
{
    int to, w;
    edge *n;
}e[N * 2], *head[N], *cur = e;
inline void addEdge(int x, int y, int w)
{
    cur->to = y, cur->w = w, cur->n = head[x], head[x] = cur++;
    cur->to = x, cur->w = w, cur->n = head[y], head[y] = cur++;
}

LL dis[N], w[N];
int size[N], deep[N], Hson[N], fa[N], top[N];
int dfn[N], tim;

void init_dfs(int x)
{
    size[x] = 1;
    for (edge *p = head[x]; p; p = p->n) if (fa[x] != p->to)
    {
        fa[p->to] = x, dis[p->to] = dis[x] + p->w, deep[p->to] = deep[x] + 1;
        init_dfs(p->to);
        size[x] += size[p->to];
        if (size[p->to] > size[Hson[x]]) Hson[x] = p->to;
    }
}

void calc_dfs(int x, int tt)
{
    dfn[x] = ++tim, w[tim] = dis[x] - dis[fa[x]], top[x] = tt;
    if (Hson[x])
    {
        calc_dfs(Hson[x], tt);
        for (edge *p = head[x]; p; p = p->n) if (fa[x] != p->to && Hson[x] != p->to)
            calc_dfs(p->to, p->to);
    }
}

struct Data
{
    LL v, k;

    Data() {}
    Data(LL v, LL k) : v(v), k(k) {}
};

namespace SegmentTree
{
#define ls (o << 1)
#define rs (o << 1 | 1)
    struct node
    {
        int l, r;
        Data val;
        LL minn;
    }t[N * 3];

    void build(int o, int l, int r)
    {
        t[o].l = l, t[o].r = r, t[o].val = Data(inf, 0), t[o].minn = inf;
        if (l == r) return ;
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
    }

#define calc(a, b) ((a).k * (b) + (a).v)
    void Set(int o, Data L)
    {
        int l = t[o].l, r = t[o].r;
        if (l == r)
            t[o].minn = min(t[o].minn, L.v);
        else if (t[o].val.v <= L.v && calc(t[o].val, w[r] - w[l]) <= calc(L, w[r] - w[l]))
            return ;
        else if (t[o].val.v >= L.v && calc(t[o].val, w[r] - w[l]) >= calc(L, w[r] - w[l]))
            t[o].val = L, t[o].minn = min(t[o].minn, calc(L, (L.k > 0 ? 0 : w[r] - w[l])));
        else
        {
            int mid = (l + r) >> 1;
            if (t[o].val.k < L.k)
            {
                if (calc(t[o].val, w[mid] - w[l]) <= calc(L, w[mid] - w[l]))
                    Set(ls, L);
                else
                {
                    Set(rs, Data(calc(t[o].val, w[mid + 1] - w[l]), t[o].val.k));
                    t[o].val = L;
                }
            }
            else
            {
                if (calc(t[o].val, w[mid] - w[l]) <= calc(L, w[mid] - w[l]))
                    Set(rs, Data(calc(L, w[mid + 1] - w[l]), L.k));
                else
                {
                    Set(ls, t[o].val);
                    t[o].val = L;
                }
            }
            t[o].minn = min(min(t[o].minn, calc(L, L.k > 0 ? 0 : w[r] - w[l])), min(t[ls].minn, t[rs].minn));
        }
    }

    void modify(int o, int l, int r, Data L)
    {
        if (t[o].l == l && t[o].r == r) 
        {
            Set(o, L); return ;
        }
        int mid = (t[o].l + t[o].r) >> 1;
        if (mid >= r)
            modify(ls, l, r, L);
        else if (mid < l)
            modify(rs, l, r, Data(calc(L, w[mid + 1] - w[t[o].l]), L.k));
        else
            modify(ls, l, mid, L), modify(rs, mid + 1, r, Data(calc(L, w[mid + 1] - w[t[o].l]), L.k));
        t[o].minn = min(t[o].minn, min(t[ls].minn, t[rs].minn));
    }

    LL query(int o, int l, int r)
    {
        if (t[o].l == l && t[o].r == r)
            return t[o].minn;

        int mid = (t[o].l + t[o].r) >> 1;
        LL ret = calc(t[o].val, (t[o].val.k > 0 ? w[l] : w[r]) - w[t[o].l]);

        if (mid >= r)
            return min(query(ls, l, r), ret);
        else if (mid < l)
            return min(query(rs, l, r), ret);
        else
            return min(min(query(ls, l, mid), query(rs, mid + 1, r)), ret);
    }

#undef calc
#undef ls
#undef rs
}

int LCA(int x, int y)
{
    while (top[x] != top[y])
    {
        if (deep[top[x]] > deep[top[y]])
            x = fa[top[x]];
        else
            y = fa[top[y]];
    }
    return deep[x] < deep[y] ? x : y;
}

void Modify(int x, int to, LL a, LL b)
{
    using SegmentTree::modify;
    while (top[x] != top[to])
    {
        modify(1, dfn[top[x]], dfn[x], Data(b - w[dfn[x]] * a, a));
        b -= (dis[x] - dis[fa[top[x]]]) * a, x = fa[top[x]];
    }
    modify(1, dfn[to], dfn[x], Data(b - w[dfn[x]] * a, a));
}

LL Query(int x, int y)
{
    using SegmentTree::query;
    LL ret = inf;
    while (top[x] != top[y])
    {
        if (deep[top[x]] < deep[top[y]]) 
            swap(x, y);
        ret = min(ret, query(1, dfn[top[x]], dfn[x])), x = fa[top[x]];
    }
    if (deep[x] > deep[y]) swap(x, y);
    return ret = min(ret, query(1, dfn[x], dfn[y]));
}

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    #endif

    int n, m;
    read(n), read(m);

    for (int i = 1, x, y, w; i < n; ++i)
    {
        read(x), read(y), read(w);
        addEdge(x, y, w);
    }

    init_dfs(1), calc_dfs(1, 1);
    SegmentTree::build(1, 1, n);
    for (int i = 2; i <= n; ++i) w[i] += w[i - 1];

    int opt, x, y, z, a, b;

    while (m--)
    {
        read(opt), read(x), read(y);
        switch (opt)
        {
            case 1 : 
                read(a), read(b), z = LCA(x, y);
                Modify(x, z, -a, b), Modify(y, z, a, (dis[x] + dis[y] - dis[z] * 2) * a + b);
                break ;
            case 2 : 
                printf("%lld\n", Query(x, y));
                break ;
        }
    }

    return 0;
}

Day 2 :

这一天的题相对简单辣

T1, BZOJ 4516

大家都说后缀自动机裸体,强行后缀数组写一发…串反转求出来 SA 后相当于逐个插入后缀…后插入的后缀也不会影响之前的后缀排序呢

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <set>

using namespace std;
typedef long long LL;
inline void read(int &x){x=0;char c;while((c=getchar())<'0'||c>'9');for(x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<1)+(x<<3)+c-'0');}

const int inf = 0x3f3f3f3f ;
const int N = 1e5 + 5;

int ch[N], n;

namespace Suffix_Array
{
    int Sa[N], Rank[N], height[N][18];
    int Log[N];

    inline bool cmp_char(int x, int y) { return ch[x] < ch[y] || (ch[x] == ch[y] && x > y); }

    void Suffix_Da()
    {
        static int ws[N];
        int i, j, p, m = 0, *x = Rank, *y = Log;

        for (i = 0; i < n; ++i) Sa[i] = i;
        sort(Sa, Sa + n, cmp_char);
        for (m = i = 1, x[Sa[0]] = 0; i < n; ++i)
            x[Sa[i]] = (ch[Sa[i - 1]] == ch[Sa[i]]) ? m - 1 : m++;

        for (j = p = 1; p < n; j <<= 1, m = p)
        {
            for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
            for (i = 0; i < n; ++i) if (Sa[i] >= j) y[p++] = Sa[i] - j;
            memset(ws, 0, sizeof(int) * (m + 1));
            for (i = 0; i < n; ++i) ++ws[x[i]];
            for (i = 1; i < m; ++i) ws[i] += ws[i - 1];
            for (i = n - 1; ~i; --i) Sa[--ws[x[y[i]]]] = y[i];
            swap(x, y), p = 1, x[Sa[0]] = 0;
        #define cmp(a, b) (y[(a)] == y[(b)] && y[(a) + j] == y[(b) + j])
            for (i = 1; i < n; ++i)
                x[Sa[i]] = cmp(Sa[i - 1], Sa[i]) ? p - 1 : p++;
        #undef cmp
        }
        for (i = 0; i < n; ++i) Rank[Sa[i]] = i;
        for (i = p = 0; i < n - 1; ++i)
        {
            p ? --p : 0;
            j = Sa[Rank[i] - 1];
            while (ch[i + p] == ch[j + p]) ++p;
            height[Rank[i]][0] = p;
        }

        Log[0] = Log[1] = 0;
        for (i = 2; i < n; ++i) Log[i] = Log[i >> 1] + 1;

        for (n--, i = 1; (1 << i) <= n; ++i)
        {
            for (j = n - (1 << i); ~j; --j)
                height[j][i] = min(height[j][i - 1], height[j + (1 << (i - 1))][i - 1]);
        }
    }

    int get_Lcp(int x, int y)
    {
        int l = Log[y - x];
        return min(height[x][l], height[y - (1 << l) + 1][l]);
    }
}

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    using namespace Suffix_Array;

    read(n);
    for (int i = n - 1; ~i; --i) read(ch[i]);

    ch[n++] = 0;
    Suffix_Da();

    LL ret = 0;

    static set<int> H;
    static set<int> :: iterator it;

    for (int i = n - 1, tmp; ~i; --i)
    {
        tmp = 0;
        it = H.lower_bound(Rank[i]);

        if (it != H.end()) 
            tmp = max(tmp, get_Lcp(Rank[i] + 1, *it));
        if (it != H.begin())
            tmp = max(tmp, get_Lcp(*(--it) + 1, Rank[i]));

        printf("%lld\n", ret += n - i - tmp);

        H.insert(Rank[i]);
    }

    return 0;
}

T2, BZOJ 4517

错排乘组合数
吐槽:数据范围有问题,怒开大几个数量级…过了但是速度垫底 233333

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
inline void read(int &x){x=0;char c;while((c=getchar())<'0'||c>'9');for(x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<1)+(x<<3)+c-'0');}

const int N = 1e7 + 1 , mo = 1e9 + 7;

int D[N + 10], fac[N + 10], inv[N + 10];

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    D[0] = D[2] = 1;
    for (int i = 3; i < N; ++i)
        D[i] = (LL) (i - 1) * (D[i - 1] + D[i - 2]) % mo;

    fac[0] = 1;
    for (int i = 1; i < N; ++i)
        fac[i] = (LL) fac[i - 1] * i % mo;

    inv[0] = inv[1] = 1;
    for (int i = 2; i < N; ++i)
        inv[i] = mo - (LL) inv[mo % i] * (mo / i) % mo;
    for (int i = 2; i < N; ++i)
        inv[i] = (LL) inv[i] * inv[i - 1] % mo;

    int T, n, m, ret;
    read(T);
    while (T--)
    {
        read(n), read(m);
        if (n < m) puts("0");
        else
        {
            ret = (LL) fac[n] * inv[m] % mo * inv[n - m] % mo * D[n - m] % mo;
            printf("%d\n", ret);
        }
    }

    return 0;
}

T3, BZOJ 4518

您推一推就会发现裸的斜率优化辣,不会炸long long 好写极辣

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
inline void read(int &x){x=0;char c;while((c=getchar())<'0'||c>'9');for(x=c-'0';(c=getchar())>='0'&&c<='9';x=(x<<1)+(x<<3)+c-'0');}

const int N = 3e3 + 5; ;

struct Point 
{
    LL x, y;

    Point() {}
    Point(LL x, LL y) : x(x), y(y) {}

    inline Point operator - (const Point & b) const
    {
        return Point(x - b.x, y - b.y);
    }
    inline double operator ^ (const Point & b) const
    {
        return (double) x * b.y - (double) y * b.x;
    }
};

struct Convex
{
    Point que[N];
    int head, tail;

    void insert(Point u)
    {
        while (head <= tail && que[tail].y >= u.y)
            --tail;
        while (head < tail && ((que[tail] - que[tail - 1]) ^ (u - que[tail])) <= 0)
            --tail;
        que[++tail] = u;
    }
    #define calc(p) (k * (p).x + (p).y)
    LL query(LL k)
    {
        while (head < tail && calc(que[head]) >= calc(que[head + 1]))
            ++head;
        return calc(que[head]);
    }
    #undef calc
} T;

int sum[N], n, m;
LL f[2][N];

int main()
{
    #ifdef LX_JUDGE
    freopen("in.txt", "r", stdin);
    #endif

    read(n), read(m);
    for (int i = 1; i <= n; ++i) 
    {
        read(sum[i]);
        sum[i] += sum[i - 1];
    }

    int t = 1;
    for (int i = 1; i <= n; ++i)
        f[t][i] = (LL) sum[i] * sum[i];

    for (int i = 1; i < m; ++i)
    {
        T.que[T.head = T.tail = 1] = Point(sum[i], f[t][i] + (LL) sum[i] * sum[i]);
        for (int j = i + 1; j <= n; ++j)
        {
            f[t ^ 1][j] = T.query(-sum[j] * 2) + (LL) sum[j] * sum[j];
            T.insert(Point(sum[j], f[t][j] + (LL) sum[j] * sum[j]));
        }
        t ^= 1;
    }

    LL ret = f[t][n] * m - (LL) sum[n] * sum[n];

    printf("%lld\n", ret);

    return 0;
}

今天没打BC………….QAQ

你可能感兴趣的:(bzoj,省选,SDOI2016)