[题解]对!我是狗!
这场考试的题解给咕掉了,但是题目质量很好啊。
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;
}