[题解]对!我是狗!

\[\color{red}{\textsf{小游者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{A small swimmer is a God.}} \\ \color{pink}{\text{The left toilet and the right eternal God}} \\ \color{pink}{\text{can break the evil energy with a sharp pen.}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{小遊者は、神であり、左便器、右永神であり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Petit voyageur, est Dieu aussi, toilettes gauche, Dieu éternel droit,}} \\ \color{lightblue}{\text{peut tenir un stylo tranchant pour briser le mal, qui devrait le faire?}} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Ein kleiner Schwimmer ist ein Gott.}} \\ \color{cyan}{\text{Die linke Toilette und der rechte ewige Gott können }} \\ \color{cyan}{\text{die böse Energie mit einem scharfen Stift brechen.}} \\ \color{cyan}{\text{Wer sollte es sein?}} \\ \hline \end{array} \\ \color{red}{\textsf{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \newcommand\group[1]{\left\langle\right\rangle} \newcommand\norm[1]{\left|{#1}\right|} \newcommand\E[0]{\mathbb{E}} \newcommand\Pro[0]{\mathbb{P}} \]

  这场考试的题解给咕掉了,但是题目质量很好啊。

No Way Out

  一旦别人说你走自己的路时,就走别人的路,让别人无路可走。

  当一个人脱离 \(s-t\) 的路径时,它的答案就被确定下来,因此,我们只需要枚举 \(\mathcal O(n)\)(大概是 \(\mathcal \Theta(2n)\)?)个先后手所在的位置,并且看看这一步到底是谁脱离了链,就可以算出来接下来的时间的答案。

人与狗

  人以利聚,狗以骨分。

  如果没有 \(S\) 的限制,就是朴素的生成树问题。

  显然,如果要满足 \(S\) 的限制,相当于所选的边集在 \(S\) 的导出子图中也应是一棵树,换言之,一颗生成树要满足一个 \(S\) 的条件是,在 \(S\) 的导出子图中有 \(\norm S-1\) 条边,对于其他任何条件也是一样。

  那么,我们可以重新构建一个图,这个图中满足 \(\displaystyle w(i,j)=\sum_{k=1}^m [i,j\in S_k]\),在这个重构图中,一颗合法的生成树必须满足 \(\sum w=\sum (\norm S-1)\),并且,在这个重构图中,所有生成树的权值都不会超过右式,因此,我们可以对这个重构图进行最大生成树计数。具体还看看代码。

/** @author __Elaina__ */

// #pragma GCC optimize("Ofast")
// #pragma GCC optimize("inline")
// #define GCC optimize(2)
// #define GCC optimize(3)

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

// #define USING_FREAD
// #define NDEBUG
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI ((char)getchar())
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}
template<class T> inline T listMax(const T& x) { return x; }
template<class T, class... Args> inline T listMax(const T& x, const Args&... args) {
    return max(x, listMax(args...));
}
template<class T> inline T listMin(const T& x) { return x; }
template<class T, class... Args> inline T listMin(const T& x, const Args&... args) {
    return min(x, listMin(args...));
}

} // namespace Elaina
using namespace Elaina;

const int Maxn = 500;
const int Maxm = 2000;
const int mod = 998244353;

inline void chkadd(int& x, int y) { if ((x += y) >= mod) x -= mod; }
inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n > 0; n >>= 1, a = (int)(1ll * a * a % mod))
        if (n & 1) ret = (int)(1ll * ret * a % mod);
    return ret;
}

int n, qry;
int c[Maxn + 5][Maxn + 5];

int sum;
bitset<Maxm + 5> S[Maxn + 5];

inline void input() {
    readin(n, qry);
    rep (i, 1, n - 1) rep (j, i + 1, n)
        readin(c[i][j]), c[j][i] = c[i][j];
    char s[Maxn + 5];
    rep (i, 1, qry) {
        scanf("%s", s + 1); int cnt = 0;
        rep (j, 1, n) S[j][i] = s[j] - '0', cnt += (s[j] == '1');
        if (cnt) sum += cnt - 1;
    }
}

int w[Maxn + 5][Maxn + 5], m;
struct edge { int u, v, w; } e[Maxn * Maxn + 5];
inline void prelude() {
    rep (i, 1, n) rep (j, i + 1, n) {
        auto mix = S[i] & S[j];
        w[i][j] = w[j][i] = mix.count();
        e[++m] = {i, j, -w[i][j]};
    }
}

class UFS {

private:

    int fa[Maxn + 5];

public:

    inline void init(int n) { rep (i, 1, n) fa[i] = i; }
    inline int find(int u) {
        while (u ^ fa[u]) u = fa[u] = fa[fa[u]];
        return u;
    }
    inline bool linkto(int u, int v) {
        u = find(u), v = find(v);
        if (u == v) return false;
        return fa[u] = v, true;
    }
    inline bool judge(int u, int v) { return find(u) != find(v); }

} ufs;

class matrixTree {

private:

    int a[Maxn + 5][Maxn + 5], id[Maxn + 5], n;

    inline int specialDet() {
        int ret = 1;
        repf (i, 1, n) { // ignore the first row
            int cur = -1;
            repf (j, i, n) if (a[j][i] != 0) { cur = j; break; }
            if (!~cur) return 0;
            swap(a[i], a[cur]), ret = (mod - ret) % mod;
            ret = (int)(1ll * ret * a[i][i] % mod);
            int inv = qkpow(a[i][i], mod - 2);
            repf (j, i + 1, n) if (a[j][i]) {
                int t = (int)(1ll * a[j][i] * inv % mod);
                repf (k, i, n) a[j][k] = (a[j][k] + mod - 1ll * a[i][k] * t % mod) % mod;
            }
        }
        return ret;
    }

public:

    inline int work(vector<int>& V, vector<edge>& E) {
        n = (int)V.size();
        repf (i, 0, n) id[V[i]] = i;
        repf (i, 0, n) repf (j, 0, n) a[i][j] = 0;
        for (auto [u, v, w]: E) {
            u = id[u], v = id[v];
            chkadd(a[u][v], w);
            chkadd(a[v][u], w);
            chkadd(a[u][u], (mod - w) % mod);
            chkadd(a[v][v], (mod - w) % mod);
        }
        return specialDet();
    }

} Func;

vector<int> vs[Maxn + 5];
vector<edge> es[Maxn + 5];
inline void solve() {
    sort(e + 1, e + m + 1, [](const edge& a, const edge& b) {
        return a.w < b.w;
    });
    ufs.init(n);
    int cnt = 0, prod = 1;
    for (int l = 1, r = 1; l <= m; r = l = r + 1) {
        rep (i, 1, n) {
            vs[ufs.find(i)] = { ufs.find(i) };
            es[i].clear();
        }
        while (r <= m && e[r].w == e[l].w) {
            const auto& [u, v, w] = e[r];
            if (ufs.judge(u, v)) {
                es[ufs.find(u)].push_back({ufs.find(u), ufs.find(v), c[u][v]});
            }
            ++r;
        }
        if (r > m || e[r].w != e[l].w) --r;
        rep (i, l, r) {
            const auto& [u, v, w] = e[i];
            if (ufs.judge(u, v)) {
                int fu = ufs.find(u), fv = ufs.find(v);
                ufs.linkto(u, v);
                es[fv].insert(es[fv].end(), es[fu].begin(), es[fu].end());
                vs[fv].insert(vs[fv].end(), vs[fu].begin(), vs[fu].end());
                es[fu].clear(), vs[fu].clear();
                cnt += (-w); // negative attention!
            }
        }
        rep (i, 1, n) {
            if (ufs.find(i) == i && !es[i].empty())
                prod = (int)(1ll * prod * Func.work(vs[i], es[i]) % mod);
            vs[i].clear(), es[i].clear();
        }
    }
    if (cnt < sum) writln(0);
    else writln(prod);
}

signed main() {
    freopen("treecnt.in", "r", stdin);
    freopen("treecnt.out", "w", stdout);
    input();
    prelude();
    solve();
    return 0;
}

底线

   「做人是要有底线的。」

   「我知道,我知道,但是我有个朋友......有个朋友,朋友的请求总不可能拒绝吧,他说他想看......」

   「磙。」

  从中选出 \(k\) 个,显然导向 wqs 二分啊。不过凸性到现在都还不会证明......

  常规操作,先二分一个系数 \(coe\),然后内层设计 DP 转移,内层设 \(f(i)\) 表示前 \(i\) 个位置都被覆盖完时的最大价值,每个 \(f(i)\) 是一个二元组 \((val,cnt)\),表示最大价值与此时选择的最多的区间。

  做 DP 的时候,枚举一下前一个位置是哪里就行了,不过需要注意的是,我们还要贪心地把那些权值为正的边选上。

  注意到可以使用线段树进行优化,于是就优化吧......其实我写这篇题解的目的还是前两道,这一道主要是记录一下 wqs 二分,所以稍微有点略。

/** @author __Elaina__ */

#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize(2)
#pragma GCC optimize(3)

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

#define USING_FREAD
#define NDEBUG
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(register int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (register int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(register int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T& rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T& rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI ((char)getchar())
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}
template<class T> inline T listMax(const T& x) { return x; }
template<class T, class... Args> inline T listMax(const T& x, const Args&... args) {
    return max(x, listMax(args...));
}
template<class T> inline T listMin(const T& x) { return x; }
template<class T, class... Args> inline T listMin(const T& x, const Args&... args) {
    return min(x, listMin(args...));
}

} // namespace Elaina
using namespace Elaina;

#define Maxn (100000)

// const int Maxn = 1e5;
const ll inf = 1e8;

int n; ll req;
int val[Maxn + 5];

inline void input() {
    readin(n, req);
    rep (i, 1, n) readin(val[i]);
}

typedef pair<ll, ll> dptype;

inline dptype operator +(const dptype& lhs, const dptype& rhs) {
    return {lhs.fi + rhs.fi, lhs.se + rhs.se};
}
inline dptype& operator +=(dptype& lhs, const dptype& rhs) {
    return lhs = {lhs.fi + rhs.fi, lhs.se + rhs.se};
}
inline dptype operator -(const dptype& lhs, const dptype& rhs) {
    return {lhs.fi - rhs.fi, lhs.se - rhs.se};
}
inline dptype& operator -=(dptype& lhs, const dptype& rhs) {
    return lhs = {lhs.fi - rhs.fi, lhs.se - rhs.se};
}

class sgtre {   // none-recursion segment tree

private:

    int m;
    dptype mx[Maxn << 2 | 2], tag[Maxn << 2 | 2];

    inline void add(int i, const dptype& x) { mx[i] += x, tag[i] += x; }
    inline void pushup(int i) {
        mx[i] = max(mx[i << 1], mx[i << 1 | 1]) + tag[i];
    }

public:

    inline void init(int n) {
        for (m = 1; m < n + 2; m <<= 1);
        drep (i, m + n + 2, 1) mx[i] = tag[i] = {0, 0};
    }
    inline void insert(int i, dptype x) {
        for (mx[i + m] = x, i = i + m >> 1; i; i >>= 1) pushup(i);
    }
    inline void modify(int l, int r, const dptype& x) {
        if (l > r) return ;
        for (l = m + l - 1, r = m + r + 1; l ^ r ^ 1;) {
            if (~l & 1) add(l ^ 1, x);
            if (r & 1) add(r ^ 1, x);
            pushup(l >>= 1), pushup(r >>= 1);
        }
    }
    inline dptype query(int l, int r) {
        dptype ql = {-inf, 0}, qr = {-inf, 0};
        if (l > r) return ql;
        for (l = m + l - 1, r = m + r + 1; l ^ r ^ 1;) {
            if (~l & 1) chkmax(ql, mx[l ^ 1]);
            if (r & 1) chkmax(qr, mx[r ^ 1]);
            ql += tag[l >>= 1], qr += tag[r >>= 1];
        }
        for (l >>= 1, chkmax(ql, qr); l; l >>= 1) ql += tag[l];
        return ql;
    }

} tre;

class mxtre {   // only used for interval max, based on none-recursion segment tree

private:

    int m;
    dptype mx[Maxn << 2 | 2];

    inline void pushup(int i) { mx[i] = max(mx[i << 1], mx[i << 1 | 1]); }

public:

    inline void init(int n) {
        for (m = 1; m < n + 2; m <<= 1);
        drep (i, m + n + 2, 1) mx[i] = {0, 0};
    }
    inline void insert(int i, dptype x) {
        for (mx[i + m] = x, i = i + m >> 1; i; i >>= 1) pushup(i);
    }
    inline dptype query(int l, int r) {
        dptype ret = {-inf, 0};
        if (l > r) return ret;
        for (l = m + l - 1, r = m + r + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
            if (~l & 1) chkmax(ret, mx[l ^ 1]);
            if (r & 1) chkmax(ret, mx[r ^ 1]);
        }
        return ret;
    }

} mxqry;

class fenwick { // statistic the positive intervals

private:

    int m;
    dptype c[Maxn + 5]; int lef[Maxn + 5];
    inline int lowbit(int i) { return i & (-i); }

public:

    inline void init(int n) {
        rep (i, 1, m = n) c[i] = {0, 0}, lef[i] = inf;
    }
    inline void insert(int i, const dptype& x, int l) {
        for (; i; i ^= lowbit(i)) c[i] += x, chkmin(lef[i], l);
    }
    inline pair<dptype, int> query(int i) {
        dptype ret = {0, 0}; int l = inf;
        for (; i <= m; i += lowbit(i)) ret += c[i], chkmin(l, lef[i]);
        return {ret, l};
    }

} bit;

ll pre[Maxn + 5], suf[Maxn + 5], buc[Maxn + 5]; int m;
inline void prelude() {
    rep (i, 1, n) pre[i] = pre[i - 1] + val[i];
    rep (i, 1, n) suf[i] = pre[n] - pre[i - 1], buc[i] = suf[i];
    sort(buc + 1, buc + 1 + n + 1);
    m = unique(buc + 1, buc + 1 + n + 1) - buc - 1;
    rep (i, 1, n + 1) suf[i] = lower_bound(buc + 1, buc + m + 1, suf[i]) - buc;
}

dptype dp[Maxn + 5];
inline dptype work(ll coe) {
    tre.init(n), mxqry.init(n), bit.init(m);
    rep (i, 1, n) dp[i] = {-inf, 0};
    dptype positive = {0, 0}, tmp;
    ll mx = -inf; int x, p;
    rep (i, 1, n) {
        bit.insert(suf[i], {buc[suf[i]], 1}, i);
        x = lower_bound(buc + 1, buc + m + 1, buc[suf[i + 1]] - coe) - buc, p;
        /** @p x the required index of suffix sum */
        /** @p p leftmost endpos of the positive intervals */
        auto reci = bit.query(x); // statistic the required positive intervals and the leftmost endpos
        tmp = reci.fi, tmp.fi += tmp.se * (coe - buc[suf[i + 1]]);
        positive += tmp;            // statistic the positive intervals
        p = min(i + 1, reci.se);    // to avoid that no positive intervals, in which case return inf
        mx = max(mx, 0ll) + pre[i] - pre[i - 1];
        tre.modify(1, i - 1, {pre[i] - pre[i - 1], 0});
        if (i > 1) mxqry.insert(i, dp[i - 1]);
        tre.insert(i, dp[i - 1] + dptype{mx + coe, 1});
        dp[i] = max(tre.query(1, p - 1), mxqry.query(p, i));
    }
    return dp[n] + positive;
}

signed main() {
    freopen("baseline.in", "r", stdin);
    freopen("baseline.out", "w", stdout);
    input();
    prelude();
    ll k = -inf; dptype reci;
    for (register ll l = -2e7, r = 2e7, mid = l + r >> 1; l <= r; mid = l + r >> 1) {
        reci = work(mid);
        if (reci.se < req) l = mid + 1;
        else k = mid, r = mid - 1;
    }
    reci = work(k);
    writln(reci.fi - req * k);
    return 0;
}
posted @ 2022-04-11 22:10  Arextre  阅读(154)  评论(0编辑  收藏  举报