A
裸的差分约束,注意用前缀和作为点的时候给前缀和一点限制,以及就是,这题卡常x
不过赛后补题我的一发过了x,队友的比赛时T飞了…
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
// a -> b (c)表示b - a <= c
int const N = 3005;
struct edge {
int w, y, next;
int is;
} e[N * 10];
int dis[N], last[N], ne;
queue<int> q;
int cnt[N];
bool inq[N];
int n, m1, m2;
inline void addedge(int x, int y, int w, int is) {
//cerr << ">> " << x << ' ' << y << ' ' << w << ' ' << is << '\n';
e[++ne] = { w, y, last[x], is };
last[x] = ne;
}
inline void add_edge_w(int delta) {
for (int i = 1; i <= ne; ++i) {
if (e[i].is == 1)
e[i].w += delta;
if (e[i].is == 2)
e[i].w -= delta;
}
}
bool chk(int delta) {
add_edge_w(delta);
fill(dis, dis + n + 1, 0);
fill(inq, inq + n + 1, 1);
fill(cnt, cnt + n + 1, 0);
while (!q.empty())
q.pop();
for (int i = 0; i <= n; ++i)
q.push(i);
while (!q.empty()) {
int now = q.front();
q.pop();
inq[now] = 0;
for (int i = last[now]; i; i = e[i].next)
if (dis[e[i].y] > dis[now] + e[i].w) {
dis[e[i].y] = dis[now] + e[i].w;
cnt[e[i].y] = cnt[now] + 1;
if (cnt[e[i].y] > n + 1) {
add_edge_w(-delta);
return 0;
}
if (inq[e[i].y] == 0) {
inq[e[i].y] = 1;
q.push(e[i].y);
}
}
}
add_edge_w(-delta);
return 1;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m1, &m2);
fill(last, last + n + 1, 0);
ne = 0;
for (int i = 1; i <= m1; ++i) {
//s[r] - s[l - 1] >= K
//s[l - 1] - s[r] <= -K
int l, r, K;
scanf("%d%d%d", &l, &r, &K);
addedge(r, l - 1, -K, 0);
}
for (int i = 1; i <= m2; ++i) {
//s[r] - s[l - 1] <= s[n] - K
int l, r, K;
scanf("%d%d%d", &l, &r, &K);
addedge(l - 1, r, -K, 1);
}
for (int i = 1; i <= n; ++i) {
//s[i] <= s[i - 1] + 1
//s[i] >= s[i - 1]
//s[i] - s[i - 1] <= 1
//s[i - 1] - s[i] <= 0
addedge(i - 1, i, 1, 0);
addedge(i, i - 1, 0, 0);
}
//s[n] - s[0] >= sn
//s[n] - s[0] <= sn
//s[0] - s[n] <= -sn
addedge(0, n, 0, 1);
addedge(n, 0, 0, 2);
int l = 0, r = n, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (chk(mid)) {
ans = mid;
r = mid - 1;
} else
l = mid + 1;
}
printf("%d\n", ans);
}
}
B
发现只需要限制端点就可以限制一个选择的区间。
也就是说,题目中的条件可以转化为,如果选择了
A
1
,
A
2
,
.
.
.
,
A
n
A_1, A_2,...,A_n
A1,A2,...,An
那么
∀
i
∈
[
1
,
n
]
\forall i \in [1, n]
∀i∈[1,n]满足
F
m
−
1
(
A
i
−
1
,
R
i
−
1
)
≥
F
m
−
1
(
A
i
,
R
i
−
1
)
F_{m - 1}(A_{i - 1}, R_{i - 1}) \geq F_{m - 1}(A_i,R_{i - 1})
Fm−1(Ai−1,Ri−1)≥Fm−1(Ai,Ri−1)
和
F
m
−
1
(
A
i
−
1
,
L
i
)
≤
F
m
−
1
(
A
i
,
L
i
)
F_{m - 1}(A_{i - 1}, L_i) \leq F_{m - 1}(A_i,L_i)
Fm−1(Ai−1,Li)≤Fm−1(Ai,Li)
用这两个条件就可以转化出一个区间,考虑DP,每次用这个区间来转移即可
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cassert>
#include <bitset>
using namespace std;
int const MOD = 1e8 + 7;
int const N = 17;
int col[1 << N];
int f[1 << N];
int g[1 << N];
int F_m(int x, int y, int m) {
int ret = 0;
for (int i = 0; i < m; ++i) {
if ((x & 1) == (y & 1))
++ret;
else
ret = 0;
x >>= 1;
y >>= 1;
}
return ret;
}
pair<int, int> b[1 << N];
/*
int c[105];
void dfs(int x, int n, int& ans, int m) {
if (x > n) {
int mul = 1;
for (int i = 1; i <= n; ++i) {
mul *= c[i];
for (int k = b[i].first; k <= b[i].second; ++k) {
for (int j = 1; j <= n; ++j)
if (F_m(c[i], k, m) < F_m(c[j], k, m)) {
cerr << ">> i j k Fi Fj : " << i << ' ' << j << ' ' << k << ' ' << F_m(c[i], k, m) << ' ' << F_m(c[j], k, m) << '\n';
cerr << "? ";
for (int p = 1; p <= n; ++p)
cerr << c[p] << ' ' ;
cerr << '\n';
return;
}
}
}
ans += mul;
return;
}
for (int i = b[x].first; i <= b[x].second; ++i) {
c[x] = i;
dfs(x + 1, n, ans, m);
c[x] = 0;
}
}
*/
int mkd_beg(int x, int y, int m) {
int ret = 0;
for (int i = m - 1; i >= 0; --i)
if ((x & (1 << i)) == (y & (1 << i)))
ret |= x & (1 << i);
else
break;
return ret;
}
int mkd_lim(int x, int y, int m) {
int ret = 0;
for (int i = m - 1; i >= 0; --i)
if ((x & (1 << i)) == (y & (1 << i))) {
if (x & (1 << i))
ret |= 1 << i;
} else
break;
--ret;
return ret;
}
int get_sum(int l, int r) {
if (l > r)
return 0;
if (l == 0)
return g[r];
return (MOD + g[r] - g[l - 1]) % MOD;
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
int m, n;
scanf("%d%d", &m, &n);
for (int i = 1; i <= n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
b[i] = { l, r };
for (int j = l; j <= r; ++j)
col[j] = i;
}
for (int i = 0; i < (1 << m); ++i) {
if (col[i] == 1) {
f[i] = i;
g[i] = i ? g[i - 1] + f[i] : f[i];
g[i] %= MOD;
continue;
}
int lim = mkd_lim(b[col[i]].first, i, m);
int beg = mkd_beg(b[col[i] - 1].second, i, m);
//cerr << "i : " << i << ' ' << beg << ' ' << lim << '\n';
beg = max(beg, b[col[i] - 1].first);
lim = min(lim, b[col[i] - 1].second);
f[i] = 1ll * i * get_sum(beg, lim) % MOD;
g[i] = (g[i - 1] + f[i]) % MOD;
}
//int chk_ans = 0;
//dfs(1, n, chk_ans, m);
//cerr << "C: " << chk_ans << '\n';
printf("%d\n", get_sum(b[n].first, b[n].second));
}
}
C
D
E
一个很傻逼的题,硬是被我写锅了x
sort 1e6怎么T嘛
不过确实存在线性做法,可以参考bzoj的mean那题
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <cassert>
using namespace std;
int const N = 1050005;
int son[N][2];
vector<int> a[N];
long long num[N];
int n;
pair<int, long long> det[N];
long long tot[N];
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
int m = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &son[i][0]);
if (son[i][0] == 1) {
son[i][1] = 0;
int t;
scanf("%d", &t);
a[i].resize(t);
for (auto& v : a[i])
scanf("%d", &v);
} else
scanf("%d%d", &son[i][0], &son[i][1]);
}
for (int i = 1; i <= n; ++i)
num[i] = 0;
num[n] = 1;
for (int i = n; i >= 1; --i)
if (!(son[i][0] == 1 && son[i][1] == 0)) {
num[son[i][0]] += num[i];
num[son[i][1]] += num[i];
}
long long sum = 0;
for (int i = 1; i <= n; ++i)
if (son[i][0] == 1 && son[i][1] == 0) {
for (auto v : a[i])
det[++m] = { v, num[i] };
sum += (long long) (a[i].size()) * num[i];
}
sort(det + 1, det + m + 1);
long long mx = 0;
long long t = 0;
for (int i = 1; i <= m; ++i) {
t += det[i].second;
if (i == n || det[i].first != det[i + 1].first) {
mx = max(mx, t);
t = 0;
}
}
//assert(mx <= sum && chk_sum == sum);
printf("%lld\n", min((sum - mx) * 2, sum));
for (int i = 1; i <= n; ++i)
// a[i].swap(std::vector<int>());
a[i].clear();
}
}
F
暴力harbin
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int const N = 7;
inline int ch_to_int(char c) {
if (c == 'h')
return 0;
if (c == 'a')
return 1;
if (c == 'r')
return 2;
if (c == 'b')
return 3;
if (c == 'i')
return 4;
if (c == 'n')
return 5;
return 6;
}
int main() {
static bool tot[7][7];
static char s[6][2000003];
int T;
scanf("%d", &T);
while (T--) {
memset(tot, 0, sizeof(tot));
for (int i = 0; i < 6; ++i) {
scanf("%s", s[i]);
for (int j = 0, m = strlen(s[i]); j < m; ++j)
tot[i][ch_to_int(s[i][j])] = 1;
}
bool ans = 0;
static int c[7];
for (int i = 0; i < 6; ++i)
c[i] = i;
do {
bool can = 1;
for (int i = 0; i < 6; ++i)
if (tot[i][c[i]] == 0) {
can = 0;
break;
}
if (can) {
ans = 1;
break;
}
} while (next_permutation(c, c + 6));
if (ans)
puts("Yes");
else
puts("No");
}
}
G
H
I
可以倒过来考虑,每次把能固定的位置固定了,然后上下界移除的时候固定移除上界就行(此时答案乘2)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cassert>
using namespace std;
using ll = long long;
int const N = 100005;
int const MOD = 1e9 + 7;
inline ll KSM(ll a, ll k) {
ll ret = 1;
a = a % MOD;
for (; k; k >>= 1, a = a * a % MOD)
if (k & 1)
ret = ret * a % MOD;
return ret;
}
int fac[N];
int fac_inv[N];
int a[N];
int n;
inline void init() {
fac[0] = 1, fac_inv[0] = 1;
for (int i = 1; i < N; ++i)
fac[i] = 1ll * fac[i - 1] * i % MOD;
for (int i = 1; i < N; ++i)
fac_inv[i] = KSM(fac[i], MOD - 2);
}
inline ll A(int n, int m) {
return 1ll * fac[n] * fac_inv[n - m] % MOD;
}
int main() {
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
init();
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
int num = 0, c = 0, ans = 1;
for (int i = n; i > 1; --i) {
if (a[i] >= n || a[i - 1] > a[i]) {
ans = 0;
break;
}
if (a[i] == a[i - 1])
++num;
else {
int minus = a[i] - a[i - 1] - 1;
if (num < minus)
ans = 0;
ans = 1ll * ans * A(num, minus) % MOD;
num -= minus;
++c;
}
}
ans = 1ll * ans * KSM(2, c) % MOD;
if (a[1] || a[2] == 0 || a[n] != n - 1)
ans = 0;
printf("%d\n", ans);
}
}
J
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
if (n <= 5)
puts("-1");
else {
if (n & 1)
printf("3 %d\n", n - 3);
else
printf("2 %d\n", n - 2);
}
}
}
K
直接按初始的情况平均分
#include <iostream>
#include <cstdio>
using namespace std;
int main() {
static int a[100005];
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
long long sum = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
sum += a[i];
}
for (int i = 1; i <= n; ++i)
printf("%.10f%c", (a[i] + 1.0 * a[i] * m / sum), (i == n ? '\n' : ' '));
}
}
L
离线,对于每个状态建trie,那么只有成为一个前缀的时候才会成为解,注意特判一下后面有0的(在trie上多建一个点)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
int const N = 5005;
int const M = 2005;
struct trie_node {
map<int, int> ch;
bool can;
} trie[2000005];
int trie_cnt;
inline int trie_newNode() {
++trie_cnt;
trie[trie_cnt].ch.clear();
trie[trie_cnt].can = 0;
return trie_cnt;
}
int dfn[N];
int a[N];
int s[N];
int trie_insert(int len) {
int p = 1;
for (int i = 0; i < len; ++i) {
if (trie[p].ch.count(s[i]) == 0)
trie[p].ch[s[i]] = trie_newNode();
p = trie[p].ch[s[i]];
}
return p;
}
int tot[N];
void clear_tot(int len) {
for (int i = len; i >= 0; --i)
tot[a[i]] = 0;
}
void trie_chk(int len) {
int p = 1, num = 0;
for (int i = len; i >= 0; --i) {
if (tot[a[i]])
continue;
++num;
tot[a[i]] = 1;
if (trie[p].ch.count(a[i]))
p = trie[p].ch[a[i]];
else
break;
trie[p].can = 1;
}
clear_tot(len);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
trie_cnt = 0;
trie_newNode();
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= m; ++i) {
int t;
scanf("%d", &t);
for (int j = 0; j < t; ++j)
scanf("%d", &s[j]);
while (t > 1 && s[t - 1] == 0 && s[t - 2] == 0)
--t;
dfn[i] = trie_insert(t);
}
for (int i = 0; i <= n; ++i)
trie_chk(i);
for (int i = 1; i <= m; ++i)
if (trie[dfn[i]].can)
puts("Yes");
else
puts("No");
}
}