终于蓝了呜呜呜
原来两场四题就能上分 以后不弃赛了
A.
注意到2, 3操作不改变总和,1操作使得总和加上了一个偶数,故直接判断总和是否为偶数即可。如果和为偶数,只要判断c和d是否相等即可。注意0要判一下
B.
先判一下奇数和偶数的个数,在按顺序分配位置就行。如果奇数和偶数个数相等,还得枚举奇数先放还是偶数先放。注意开ll。
C.
莫名其妙地ac了,不是很懂。
首先枚举左端点l和右端点r,用函数check(l,r)统计这两个端点的贡献(在l处开始,r处结束的合法括号序列的数量)。假设每一个左括号为1,右括号为-1,对序列求个前缀和,方便判断是否合法。
所有的从l开始,r结束的合法括号序列,不一定用了l和r中的每一个左右括号。
首先计算d=sum[r]-sum[l-1],以得出左右两边是否有多余的括号,我们要舍去。之后再枚举\(l+1\)到\(r-1\)的每一个位置\(i\),他们必须满足\(sum[i]-sum[l-1]>=0\).统计这个差值的最小值,记为mx,其表示左边l处最多能有几个括号多出来。那么答案至多有mx+1.注意当d>0时,这个值要减去d后才能用于更新mx。
之后保险起见,还得和左边能贡献的左括号数量,右边能贡献的右括号数量取min,注意此时也要把d的偏差加上去,具体见代码。
// Problem: C. Compressed Bracket Sequence
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/C
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ll long long
int rd() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
int n, m, k;
ll tot;
ll c[maxn], ans, sum[maxn];
ll check(int l, int r) {
ll d = sum[r]-sum[l-1], mx = 99999999999999999ll;
for (int i = l+1; i < r; i += 2) {
if (d <= 0)
mx = min(mx, sum[i]-sum[l-1]);
if (d > 0)
mx = min(mx, sum[i]-d-sum[l-1]);
}
if (mx < 0) return 0;
//if (mx == 0) return 1;
if (d == 0) return min(c[l], min(mx + 1ll, -c[r]));
if (d < 0) return min(c[l], min(mx + 1ll, -c[r]+d));
if (d > 0) return min(c[l]-d, min(mx+1ll, -c[r]));
}
int main() {
n = rd();
for (int i = 1; i <= n; i++) {
c[i] = rd();
if (i % 2 == 0) c[i] = -c[i];
sum[i] = sum[i-1]+c[i];
}
for (int l = 1; l <= n; l += 2) {
for (int r = l+1; r <= n; r += 2) {
ans += check(l, r);
}
}
printf("%lld\n", ans);
}
D.
\(a+b=a\&b+a|b\),用\(a+b,a+c,b+c\)可以求出\(abc\),已知\(a\)和\(a\&b+a|b\)可以求\(b\),这样直接求出所有的\(a[i]\)就行。
// Problem: D. Take a Guess
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 7;
#define ll long long
int rd() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
int n, m, k, tot;
ll a[maxn];
typedef pair<int, int> pii;
#define mk make_pair
ll getPlus(int i, int j) {
printf("or %d %d\n", i, j);
fflush(stdout);
ll orij = rd();
printf("and %d %d\n", i, j);
fflush(stdout);
ll andij = rd();
return orij + andij;
}
void get(int i, int j, int k) {
ll pij = getPlus(i, j);
ll pjk = getPlus(j, k);
ll pik = getPlus(i, k);
a[i] = (pij - pjk + pik) / 2ll;
a[j] = (pjk - pik + pij) / 2ll;
a[k] = (pik - pij + pjk) / 2ll;
}
int main() {
n = rd(); k = rd();
for (int i = 1; i <= n; i += 3) {
if (i + 2 > n) break;
get(i, i+1, i+2);
}
if (n % 3 == 1) {
ll p1 = getPlus(1, n);
a[n] = p1 - a[1];
}
if (n % 3 == 2) {
ll p1 = getPlus(1, n-1);
a[n-1] = p1 - a[1];
ll p2 = getPlus(1, n);
a[n] = p2 - a[1];
}
sort(a+1, a+n+1);
printf("finish %lld\n", a[k]);
return 0;
//fflush(stdout);
}
注意每一行输出之后要立刻fflush(stdout);
E
给出俩个长为n的整数序列a和b,以及一些询问(l,r). 对于每个\((l,r)\),回答最少要执行多少次操作,使得a[l...r]=b[l...r],或是判断无解。
每次操作为选取偶数个下标\(pos_i\),满足\(l\le pos_1 < pos_2 < \cdots <pos_k \le r\),然后让a中下标为\(pos_1,pos_3,pos_5,\cdots, pos_k-1\)的数+1,让b中下标为\(pos_2,pos_4,pos_6,\cdots,pos_k\)的位置的数+1.
每次询问是互相独立的。
sol:
每次操作不改变\(\sum_{i=l}^{r}(a[i]-b[i])[a[i]\ne b[i]]\),如果初始时这个差不等于0,那么必无解。
令\(c[i]=a[i]-b[i]\),操作变为令\(c[pos_{2i-1}]+=1, c[pos_{2i}]-=1\),有解的充分条件变为\(\sum_{i=l}^{r}c[i] =0\).
其实c题给出了这道题的提示,如果把小于0的\(c[i]\)看成是\(c[i]\)个\('('\)号,大于0的\(c[i]\)看成是\(c[i]\)个\(')'\)号,那么每次操作可以看成从括号序列中取出若干对形如\("()()\cdots()"\)的子序列删除。由于每次取出的子序列是一个合法括号序列,它们在不改变子序列内元素相对顺序的情况下任意组合,得到的一定也是一个合法括号序列(左括号数永远大于等于已经出现的右括号数,并且最终左括号数等于右括号数)。记\(\sum_{i=1}^{n}c[i]=sum[n]\),那么询问\((l,r)\)有解,当且仅当\(\forall i \in [l, r], i\in \N, sum[i]-sum[l-1]\le 0\),并且\(sum[r]-sum[l-1]=0\).即前缀和的最大值小于等于0且区间和等于0即可。不带修改的情况下查找区间最值用ST表实现。最小次数即前缀和的最小值(每一个前缀中的左括号只能归到一次操作的起点)
// Problem: E. Equilibrium
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1556/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ll long long
int n, m, k, tot, a[maxn], q;
ll sum[maxn];
ll stmx[22][maxn], stmi[22][maxn];
int rd() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
ll querymi(int l, int r) {
int len = r - l + 1, len2;
for (len2 = 0; (1 << (len2+1)) < len; len2++);
return min(stmi[len2][l], stmi[len2][r-(1<<len2)+1]);
}
ll querymx(int l, int r) {
int len = r - l + 1, len2;
for (len2 = 0; (1 << (len2+1)) < len; len2++);
return max(stmx[len2][l], stmx[len2][r-(1<<len2)+1]);
}
int main() {
n = rd();
q = rd();
//printf("%d %d\n", n, q);
for (int i = 1; i <= n; i++)
a[i] = rd();
for (int i = 1; i <= n; i++) {
a[i] -= rd();
sum[i] = sum[i-1] + a[i];
stmx[0][i] = stmi[0][i] = sum[i];
}
//q = rd();
for (int j = 1; j <= 20; j++)
for (int i = 1; i <= n-(1<<j)+1; i++)
stmi[j][i] = 100000000000001ll, stmx[j][i] = -100000000000001ll;
for (int i = 1; i <= 20; i++) {
for (int j = 1; j <= n - (1<<i)+1; j++) {
stmi[i][j] = min(stmi[i-1][j], stmi[i-1][j+(1<<(i-1))]);
stmx[i][j] = max(stmx[i-1][j], stmx[i-1][j+(1<<(i-1))]);
}
}
while (q--) {
int l = rd();
int r = rd();
//printf("l == %d r == %d qmx == %lld qmi == %lld\n", l, r, querymx(l, r)-sum[l-1], querymi(l, r)-sum[l-1]);
if (sum[r]-sum[l-1]!=0 || querymx(l, r) > sum[l-1]) {
puts("-1");
continue;
} else {
printf("%lld\n", -(querymi(l, r)-sum[l-1]));
}
}
return 0;
}
F
n个点,每个点有一个权值\(a_i\),任意俩点间均有边,边的方向定义如下:\(i\)与点\(j\)之间的边有\(\frac{a_i}{a_i+a_j}\)的概率由\(i\)到\(j\),求图中能到达其他所有点的点数的期望。
sol:
题解 CF1556F 【Sports Betting】 - juju 的博客 - 洛谷博客 (luogu.org)
Deltix Round, Summer 2021. Editorial - Codeforces
题面中建出来的图叫竞赛图,即为无向完全图中的每一条边分配方向。“两两之间比赛总能决定出一个胜者”。
性质:一个竞赛图的强连通分量缩点之后,拓扑序唯一(缩点之后是一条链状物)。
证:考虑有向边\((u,v)\),假设\(u\),\(v\)所在的强连通分量不同,\(u\)所属的强连通分量和\(v\)所属的强连通分量(分别记为\(A,B\))之间的拓扑序一定是\(A<B\).\(A\)中其它的和\(B\)相连的边一定也是由\(A\)指向\(B\),否则它们会在同一个强连通分量中。 故他们就会变成缩点后同一条链上前后俩点(中间可能有其他点)。
一个点能到达其他所有点,说明以其为根的叶向生成树存在(缩点后这个点属于链的起点对应的强连通分量)。
枚举某个点集\(S\)作为缩点后的链上的起点,记其为起点的概率为\(f(S)\),那么\(S\)对答案的贡献为\(|S|\times f(S)\).
记\(p(S,T)\)表示\(S\)中的所有与\(T\)相连的边都是自\(S\)连向\(T\)的概率,即\(S\)在链上的位置在\(T\)之前的概率,\(p\)是好求的。则\(p(S, U/S)\)表示\(S\)恰好构成SCC链上从起点开始的连续一段的概率,记为\(g(S)\)。它比\(f(S)\)多计算了\(S\)被分为大于1个SCC的概率.
多计算的概率可以通过枚举\(S\)的非空真子集\(s\)作为这条SCC链的起点,那么子集\(s\)对\(f(S)\)多做的贡献为\(f(s)g(S-s)/p(S-s, s)\).
\(g(S-s)\)中多计算了\(S-s\)与\(s\)间的边全为\((S-s)->s\)的概率,这个与实际相反,而\(S-s\)与\(s\)间的边全为\(s->(S-s)\)的概率已经在\(f(s)\)中计算过,因此除去\(p(S-s, s)\)即可。
剩下的就是状压dp了。
我大受震撼
如果暴力计算\(p\)和\(g\)的话是\(O(3^n\times n^2)\)的,我自带巨大常数在第9个点TLE了。但可以选择交C++17(64)水过去。
考虑预处理出\(p\)
记\(H(x, T)\)表示点\(x\)与集合\(T\)中的点之间的边都是\(x->T\)的概率。则
\(H\)可以在\(O(2^n\times n^2)\)时间内预处理,这样计算\(f\)的时间就能优化到\(O(3^n\times n)\), 总复杂度降至\(O(3^nn+2^nn^2)\)
\(p\)还可以进一步优化至\(O(1)\)求。
考虑把\(n\)分成大小差不超过1的两个集合, 分别记为\(A,B\)
记\(S,T\)在\(A,B\)中的部分分别为\(S_A, S_B, T_A, T_B\),那么
这四部分可以分别预处理出来
要注意\(S_A,S_B,T_A,T_B\)可能为0, 这种情况要特别处理一下,有点难调试;
// Problem: F. Sports Betting
// Contest: Codeforces - Deltix Round, Summer 2021 (open for everyone, rated, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/1556/problem/F
// Memory Limit: 256 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 17, md = 1e9 + 7, N = (1 << 14) + 7, M = (1 << 7) + 7;
#define ll long long
int n, a[maxn], U1, U, U2, m;
ll f[N], g[N], h[maxn][N], paa[M][M], pab[M][M], pba[M][M], pbb[M][M], inv[2000007], ans;
int rd() {
int s = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {s = s * 10 + c - '0'; c = getchar();}
return s * f;
}
ll ksm(ll a, ll b) {ll res = 1; while (b) {if (b & 1) res = res * a % md; a = a * a % md; b >>= 1;} return res;}
ll P(int s, int t) {
int sa = s & U1, ta = t & U1, sb = (s & U2) >> m, tb = (t & U2) >> m;
return max(paa[sa][ta], 1ll)*max(pab[sa][tb], 1ll)%md*max(pba[sb][ta], 1ll)%md*max(pbb[sb][tb], 1ll)%md;
}
int main() {
n = rd(); m = n / 2;
U = (1 << n) - 1; U1 = (1 << m) - 1; U2 = U ^ U1;
for (int i = 0; i < n; i++) a[i] = rd();
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (!inv[a[i]+a[j]])
inv[a[i]+a[j]] = ksm(a[i]+a[j], md - 2);
for (int i = 0; i < n; i++) {
h[i][0] = 1;
for (int S = 1; S <= U; S++) {
if (S & (1 << i)) continue;
h[i][S] = 1;
for (int j = 0; j < n; j++) {
if (S & (1 << j)) {
h[i][S] = h[i][S] * a[i] % md * inv[a[i]+a[j]] % md;
}
}
}
}
for (int s = 0; s <= U1; s++) {
int us = U1 ^ s;
paa[s][0] = 1;
for (int t = us; t; t = (t-1) & us) {
paa[s][t] = 1;
for (int i = 0; i < m; i++) {
if (s & (1 << i)) {
paa[s][t] = paa[s][t] * h[i][t] % md;
}
}
}
}
for (int s = 0; s <= U1; s++) { //U1==2-1=1
pab[s][0] = 1;
for (int t = 0; t <= U2>>m; t++) {
pab[s][t] = 1;
for (int i = 0; i < m; i++) {
if (s & (1 << i)) {
pab[s][t] = pab[s][t] * h[i][t<<m] % md;
}
}
}
}
for (int s = 0; s <= U2 >> m; s++) {
pba[s][0] = 1;
for (int t = 0; t <= U1; t++) {
pba[s][t] = 1;
for (int i = 0; i < n-m; i++) {
if (s & (1<<i)) {
pba[s][t] = pba[s][t] * h[i+m][t] % md;
}
}
}
}
for (int s = 0; s <= U2 >> m; s++) {
int us = (U2>>m)^s;
pbb[s][0] = 1;
for (int t = us; t; t = (t-1) & us) {
pbb[s][t] = 1;
for (int i = 0; i < n-m; i++) {
if (s & (1 << i)) {
pbb[s][t] = pbb[s][t] * h[i+m][t<<m] % md;
}
}
}
}
for (int i = 0; i <= U; i++) g[i] = P(i, U^i);
for (int S = 1; S <= U; S++) {
f[S] = g[S];
for (int s = (S-1)&S; s; s = (s-1) & S) {
f[S] = (f[S] - f[s] * g[S-s] % md * ksm(P(S-s, s), md - 2) % md + md) % md;
}
int cnt = 0;
for (int i = 0; i < n; i++) if (S & (1 << i)) cnt++;
ans = (ans + 1ll * cnt * f[S] % md) % md;
}
printf("%lld\n", ans);
return 0;
}
然后跑得比C++17 (64)的\(O(n^23^n)\)慢hhh