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