[题解]对!我是狗!

小游者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?A small swimmer is a God.The left toilet and the right eternal Godcan break the evil energy with a sharp pen.Who can resist him? 小遊者は、神であり、左便器、右永神であり鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? Petit voyageur, est Dieu aussi, toilettes gauche, Dieu éternel droit,peut tenir un stylo tranchant pour briser le mal, qui devrait le faire?Der Direktor ist wirklich ein Gottmit einer Toilette links und Yongshen rechtsder einen spitzen Stift hältum die Wahrheit zu durchdringen.Wer kann ihm widerstehen? Ein kleiner Schwimmer ist ein Gott.Die linke Toilette und der rechte ewige Gott können die böse Energie mit einem scharfen Stift brechen.Wer sollte es sein?对曰:“无人,狗欲当之,还请赐教!”

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

No Way Out

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

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

人与狗

  人以利聚,狗以骨分。

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

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

  那么,我们可以重新构建一个图,这个图中满足 w(i,j)=k=1m[i,jSk],在这个重构图中,一颗合法的生成树必须满足 w=(|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 @   Arextre  阅读(161)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示