終於藍了嗚嗚嗚
原來兩場四題就能上分 以后不棄賽了
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