[LibreOJ 2146][SHOI2017]寿司餐厅
建了半天图……搞出来了几个神奇的最小割……
然后发现这TM不就是最简单的最大权闭合子图吗……
首先和正负点权普通的最大权闭合子图一样,然后任意\(d_{i, i}\)去依赖点\(i\)。对于一个\(d_{i, j}(i < j)\),我们要保证那一天\([i, j]\)全部被吃了,所以说它需要依赖\(d_{i + 1, j}\)和\(d_{i, j - 1}\)。
每种寿司的费用也不难处理,我们把\(mx^2\)和\(cx\)分开考虑。\(mx^2\)单独建点,很显然代号为所有\(x\)都要依赖它。对于\(cx\),可以视为所有点有一个\(x\)的费用。
然后van了……
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
const int maxn = 5000005;
const int maxm = 5000005;
int first[maxn];
int next[maxm], to[maxm], cap[maxm], flow[maxm];
int gcnt = 0;
void add_edge(int u, int v, int f) {
gcnt ++;
next[gcnt] = first[u];
first[u] = gcnt;
to[gcnt] = v;
cap[gcnt] = f;
flow[gcnt] = 0;
}
int rev(int i) {
if(1 & i) {
return i + 1;
} else {
return i - 1;
}
}
void ins_edge(int u, int v, int f) {
add_edge(u, v, f); add_edge(v, u, 0);
}
int s, t;
int d[maxn];
bool bfs() {
#ifdef LOCAL
// puts("A new round :");
#endif
static bool vis[maxn];
memset(vis, 0, sizeof(vis));
d[s] = 1; vis[s] = true;
std::queue<int> Q; Q.push(s);
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = first[u]; i; i = next[i]) {
int v = to[i];
if(cap[i] > flow[i] && !vis[v]) {
d[v] = d[u] + 1; vis[v] = true;
#ifdef LOCAL
// printf("d[%d] : %d\n", v, d[v]);
#endif
Q.push(v);
}
}
}
return vis[t];
}
int cur[maxn];
int dfs(int u, int a) {
#ifdef LOCAL
// printf("State (%d, %d)\n", u, a);
#endif
if(a == 0 || u == t) return a;
int fl = 0;
for(int &i = cur[u]; i; i = next[i]) {
int v = to[i]; int f;
if(d[v] == d[u] + 1 && (f = dfs(v, std::min(cap[i] - flow[i], a))) > 0) {
fl += f; a -= f;
flow[i] += f; flow[rev(i)] -= f;
if(a == 0) break;
}
}
if(a > 0) d[u] = -1;
return fl;
}
int tot;
int dinic() {
int ans = 0;
while(bfs()) {
for(int i = 0; i <= tot; i ++) cur[i] = first[i];
ans += dfs(s, 0x7fffffff);
}
return ans;
}
int ma_p[105], ma_m[1005];
int ma_d[105][105];
int main() {
tot = 1;
int n, m; scanf("%d%d", &n, &m);
int ans = 0;
s = 0, t = 1;
for(int i = 1; i <= n; i ++) {
int a; scanf("%d", &a);
ma_p[i] = ++ tot;
#ifdef LOCAL
printf("p[%d] : %d\n", i, tot);
#endif
if(!ma_m[a]) {
ma_m[a] = ++ tot;
#ifdef LOCAL
printf("m[%d] : %d\n", a, tot);
#endif
ins_edge(tot, 1, m * a * a);
}
ins_edge(ma_p[i], 1, a);
ins_edge(ma_p[i], ma_m[a], 0x7fffffff);
}
for(int i = 1; i <= n; i ++) {
ma_d[i][i] = ++ tot;
for(int j = i + 1; j <= n; j ++) {
ma_d[i][j] = ++ tot;
#ifdef LOCAL
printf("ma_d[%d][%d] : %d\n", i, j, tot);
#endif
}
}
for(int i = 1; i <= n; i ++) {
int d_i; scanf("%d", &d_i);
if(d_i >= 0) {
ans += d_i;
ins_edge(0, ma_d[i][i], d_i);
} else {
ins_edge(ma_d[i][i], 1, -d_i);
}
ins_edge(ma_d[i][i], ma_p[i], 0x7fffffff);
for(int j = i + 1; j <= n; j ++) {
scanf("%d", &d_i);
int np = ma_d[i][j];
if(d_i >= 0) {
ans += d_i;
ins_edge(0, np, d_i);
ins_edge(np, ma_d[i][j - 1], 0x7fffffff);
ins_edge(np, ma_d[i + 1][j], 0x7fffffff);
} else {
ins_edge(np, 1, -d_i);
ins_edge(np, ma_d[i][j - 1], 0x7fffffff);
ins_edge(np, ma_d[i + 1][j], 0x7fffffff);
}
}
}
ans -= dinic();
#ifdef LOCAL
for(int i = 0; i <= tot; i ++) {
for(int j = first[i]; j; j = next[j]) {
if(!(j & 1)) continue;
int v = to[j];
if(cap[j] == flow[j]) {
printf("Edge (%d, %d, %d) deleted.\n", i, v, cap[j]);
}
}
}
#endif
printf("%d\n", ans);
return 0;
}
[LibreOJ 2142][SHOI2017]相逢是问候
f**********ck我终于肝掉这道题了……在这种辣鸡题上浪费了大量时间
首先一堆\(c\)套起来的幂让我们想到了上帝与集合的正确用法那道题(这题题解我屯到现在没写,还是算了吧就写这题得了)……那让我们试试欧拉降幂公式?
观察欧拉降幂公式
\[a^x\equiv a^{x\mod \varphi(m) + \varphi(m)}\pmod{m}(a\geq m)\]
在多次降幂之后,最后肯定存在一个膜数为1,最后在对这一个地方进行操作的话,虽然\(A[i]\)的位置会被移到更高的次幂上,但是开始出现膜数1的地方一定只能取到1了,所以再对这些地方操作是不合理的。
每次取\(\varphi(x)\)都会使数至少下降一半,所以一个数最多被操作\(\log_2 p\)次。
然后我就跪了(虽然在BZOJ上水过去了),后来手肝gprof发现瓶颈在快速幂(逃
然后这TM怎么优化……膜了一发题解发现正解非常的KBS……
对于一个幂,他的指数肯定可以表示为\(2^{16} a + b\)且\(a\)最大的形式……然后我们可能用到的膜数是很少的,对于每种膜数大力预处理可能的\(b\)的答案和\(a\)的答案,然后求一次幂就\(O(1)\)了……
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <utility>
#include <climits>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
const int maxn = 50005;
const int maxno = maxn << 2;
const int siz = 65536;
typedef long long ll;
inline ll phi(ll x) {
ll bd = sqrt(x + 0.5);
ll ans = x;
for(ll i = 2; i <= bd; i ++) {
if(x % i == 0LL) {
ans = ans / i * (i - 1LL);
while(x % i == 0LL) {
x /= i;
}
}
}
if(x > 1LL) ans = ans / x * (x - 1LL);
return ans;
}
int n, m;
ll p, c;
ll f[100]; int fn, bd;
ll pow1[70][siz], pow2[70][siz];
inline void process() {
f[0] = p; fn = 1LL;
for(int i = 1; ; i ++) {
f[i] = phi(f[i - 1]);
if(f[i] == 1LL) {
fn = i;
break;
}
}
ll tmp = 1LL; bd = 0;
if(c != 1LL) {
while(tmp * c < p) {
bd ++; tmp *= c;
}
} else {
bd = 0x7fffffff;
}
f[69] = LLONG_MAX;
for(int i = 0; i <= fn; i ++) {
ll c1 = c % f[i]; ll c2 = c1;
int t = 16; while(t --) c2 = (c2 * c2) % f[i];
pow1[i][0] = 1LL; if(i == fn) pow1[i][0] = 0;
for(int j = 1; j < siz; j ++) pow1[i][j] = (pow1[i][j - 1] * c1) % f[i];
pow2[i][0] = 1LL; if(i == fn) pow2[i][0] = 0;
for(int j = 1; j < siz; j ++) pow2[i][j] = (pow2[i][j - 1] * c2) % f[i];
}
int i = 69;
for(int g = 0; g <= 0; g ++) {
ll c1 = c % f[i]; ll c2 = c1;
int t = 16; while(t --) c2 = (c2 * c2) % f[i];
pow1[i][0] = 1LL;
for(int j = 1; j < siz; j ++) pow1[i][j] = (pow1[i][j - 1] * c1) % f[i];
pow2[i][0] = 1LL;
for(int j = 1; j < siz; j ++) pow2[i][j] = (pow2[i][j - 1] * c2) % f[i];
}
}
inline ll pow_mod(ll a, ll b, ll p) {
return (pow1[p][b & (siz - 1)] * pow2[p][b >> 16]) % f[p];
}
ll A[maxn];
int be_done[maxn]; ll sumv[maxno];
bool break_down[maxno];
inline void maintain(int o) {
int lc = o << 1, rc = o << 1 | 1;
sumv[o] = (sumv[lc] + sumv[rc]);
if(sumv[o] >= p) sumv[o] -= p;
break_down[o] = (break_down[lc] && break_down[rc]);
}
inline ll get_ans(int i) {
register ll up;
for(int j = be_done[i]; j >= 0; j --) {
if(j == be_done[i]) {
up = A[i];
} else {
if(up > bd) {
up = pow_mod(c % f[j], up, j) + f[j];
} else {
up = pow_mod(c, up, 69);
}
}
}
return up;
}
void build_tree(int o, int L, int R) {
if(L == R) {
be_done[L] = 0;
break_down[o] = 0;
sumv[o] = A[L];
} else {
int M = (L + R) / 2;
build_tree(o << 1, L, M);
build_tree(o << 1 | 1, M + 1, R);
maintain(o);
}
}
int ql, qr;
void modify(int o, int L, int R) {
if(L == R) {
be_done[L] ++;
if(c == 1LL) {
sumv[o] = 1LL;
break_down[o] = true;
} else {
sumv[o] = get_ans(L) % p;
if((A[L] != 0LL && be_done[L] == fn) || (A[L] == 0LL && be_done[L] == fn + 1)) break_down[o] = true;
}
} else {
int M = (L + R) / 2;
if(ql <= M && !break_down[o << 1]) {
modify(o << 1, L, M);
}
if(qr > M && !break_down[o << 1 | 1]) {
modify(o << 1 | 1, M + 1, R);
}
maintain(o);
}
}
ll query(int o, int L, int R) {
if(ql <= L && R <= qr) {
return sumv[o];
} else {
int M = (L + R) / 2;
ll ans = 0;
if(ql <= M) {
ans = (ans + query(o << 1, L, M));
if(ans >= p) ans -= p;
}
if(qr > M) {
ans = (ans + query(o << 1 | 1, M + 1, R));
if(ans >= p) ans -= p;
}
return ans;
}
}
int main() {
// freopen("17.in", "r", stdin);
// freopen("dummy", "w", stdout);
scanf("%d%d%lld%lld", &n, &m, &p, &c);
process();
for(int i = 1; i <= n; i ++) scanf("%lld", &A[i]);
build_tree(1, 1, n);
while(m --) {
int op; scanf("%d%d%d", &op, &ql, &qr);
if(op == 0) {
modify(1, 1, n);
} else {
printf("%lld\n", query(1, 1, n));
// fflush(stdout);
}
}
return 0;
}