排兵佈陣
(過水已隱藏)
光線
算法:數學、物理。
忽略\(\%\)爲了行文方便。一種直接的方法:構建圖以及概率的轉移。定義光線方向朝下,且在第\(i\)個玻璃和\(i+1\)個玻璃之間(\(i=0\)表示第一塊玻璃的上方,\(i=n\)表示在最後一塊玻璃的下方)的光線總數爲\(f_i\)。那麼我們想要的答案即爲\(f_n\)。然後我們列出來轉移,發現有
其中邊界
其中\(dis_{i\to j}\)表示光線在\(i\)和\(i+1\)塊玻璃間且朝下反射後朝上到達了\(j\)和\(j+1\)塊玻璃之間後又回到了第\(i\)和\(i+1\)塊玻璃之間,不難列出來
這樣不重不漏地分成了從上往下和從下返回到上回到\(j\)的兩種情況。直接高消可以通過\(50pts\)的\(n\leqslant 100\)。不難發現變量只與後綴的\(f\)有關,相當於消元矩陣已經變成上三角的了,我們可以採用回代的方式解出來\(f_n\)。具體的就是把上式先變形:
題目保證\(a_i\ne0\)。這個\(\sum_{i\leqslant j<n}f_j\times dis_{i\to j}\)是可\(\mathcal O(1)\)轉移的。然後用\(f_i\)表示\(f_{i-1}\),最後我們可以得到\(f_0\)與\(f_n\)的關係式。求個逆元即可。複雜度\(\mathcal O(n)\)。
#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i)
#define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i)
#define per0(i, a) for (int i = a-1; ~i; --i)
#define chkmax(a, b) a = std::max(a, b)
#define chkmin(a, b) a = std::min(a, b)
const int maxn = 555555;
const int P = 1e9 + 7;
int n, a[maxn], b[maxn], inva[maxn], f[maxn];
int qpow(int a, int b) {
int res = 1;
for (int i = a; b; i = 1ll*i*i%P, b >>= 1)
if (b & 1) res = 1ll*res*i%P;
return res;
}
#define inv(x) qpow(x, P-2)
const int inv100 = inv(100);
int main() {
n = read();
rep(i, 1, n) a[i] = 1ll*read()*inv100%P, b[i] = 1ll*read()*inv100%P, inva[i] = inv(a[i]);
f[n] = 1;
int suf = 0;
per(i, n, 1) {
f[i-1] = 1ll*(f[i]-1ll*suf*b[i]%P+P)*inva[i]%P;
suf = (1ll*suf*a[i] + 1ll*f[i-1]*b[i])%P;
}
printf("%d", inv(f[0]));
return 0;
}
其中suf
維護\(\sum_{i\leqslant j<n}f_i\times b_{i+1}\prod_{j<k\leqslant i}a_k\),表示與\(f_n\)的關係,不順道維護\(b_j\)原因在於轉移時可能會遇到\(b_j=0\)的情況導致除不了(我第一把30pts的理由)。
還有一種極妙的方法:既然是一道物理題,我們通過物理中等效替代法,將兩塊玻璃合併成一塊玻璃。不難看出總透過率爲在兩塊玻璃之間往復0次、1次、...相當於一個無窮級數求和,同理反射率也爲如此,最後可以得到
合併到只剩一塊玻璃即可。
刪數
算法:線段樹(維護區間0的個數)
先統計\(a_i=k\)的數的個數,記作\(b_i\)。對於所有的\(b_i\),我們將\([i-b_i+1,b_i]\)全部打上標記,最終\([1,n]\)沒有打上標記的位置就是答案。證明顯然,消除完的充要條件爲\([1,n]\)內全部打上標記,然後每次你只能移一個重複覆蓋的數去填一個沒標記的位置。特殊的就是對於\(a_i>n\)的情況,這個不能參與打標記的部分(它不可能參與刪除)
然後就是拿一顆線段樹來維護這樣的東西,由於有修改操作,我們不能維護0/1,而要維護區間加和區間查詢0的個數。這裏通過維護區間最小值以及最小值的個數(因爲區間最小值只能爲0,可用該方法),然後進而維護。對於\(i>n\)的情況,只有數組整體修改時會出現,此時把它在線段樹上去掉即可,反之亦然。
稍微麻煩的就是操作二,此時記一個offset
表示整體偏移,然後其它的依此調整。
複雜度\(\mathcal O(n\log n)\)。一定要注意空間大小、位置的計算!
#include <bits/stdc++.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define rep(i, a, b) for (int i = a, i##end = b; i <= i##end; ++i)
#define per(i, a, b) for (int i = a, i##end = b; i >= i##end; --i)
#define rep0(i, a) for (int i = 0, i##end = a; i < i##end; ++i)
#define per0(i, a) for (int i = a-1; ~i; --i)
#define chkmax(a, b) a = max(a, b)
#define chkmin(a, b) a = min(a, b)
const int maxn = 155555;
#define ls (o << 1)
#define rs (o << 1 | 1)
int buf[maxn << 2];
int n, m, a[maxn], *b, offset = 0;
int minv[maxn << 4], cnt[maxn << 4], val[maxn << 4], tag[maxn << 4];
void build(int o, int l, int r) {
minv[o] = tag[o] = 0, cnt[o] = val[o] = r-l+1;
if (l == r) return;
int mid = l+r>>1;
build(ls, l, mid), build(rs, mid+1, r);
}
void pushup(int o) {
val[o] = val[ls] + val[rs];
minv[o] = min(minv[ls], minv[rs]);
cnt[o] = (minv[o] == minv[ls] ? cnt[ls] : 0) + (minv[o] == minv[rs] ? cnt[rs] : 0);
}
void pushdown(int o) {
if (!tag[o]) return;
tag[ls] += tag[o], tag[rs] += tag[o]; minv[ls] += tag[o], minv[rs] += tag[o];
val[ls] = minv[ls] ? 0 : cnt[ls]; val[rs] = minv[rs] ? 0 : cnt[rs];
tag[o] = 0;
}
void modify(int o, int l, int r, int ql, int qr, int v) {
if (ql <= l && r <= qr) {
tag[o] += v; val[o] = (minv[o] += v) ? 0 : cnt[o];
return;
}
pushdown(o);
int mid = l+r>>1;
if (ql <= mid) modify(ls, l, mid ,ql, qr, v);
if (mid < qr) modify(rs, mid+1, r, ql, qr, v);
pushup(o);
}
int query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return val[o];
pushdown(o);
int mid = l+r>>1, res = 0;
if (ql <= mid) res += query(ls, l, mid ,ql, qr);
if (mid < qr) res += query(rs, mid+1, r, ql, qr);
return res;
}
void modify(int l, int r, int v) { modify(1, 1, (n+m)<<1, l+m+n, r+m+n, v); }
int query(int l, int r) { return query(1, 1, (n+m)<<1, l+m+n, r+m+n); }
int main() {
n = read(); m = read(); b = &buf[m];
build(1, 1, (n+m)<<1);
rep(i, 1, n) b[a[i] = read()]++;
rep(i, 1, n) modify(i-b[i]+1, i, 1);
rep(i, 1, m) {
int p = read(), v = read();
if (p) {
if (a[p]-offset <= n) modify(a[p]-b[a[p]]+1, a[p]-b[a[p]]+1, -1);
b[a[p]]--, b[a[p] = v+offset]++;
if (a[p]-offset <= n) modify(a[p]-b[a[p]]+1, a[p]-b[a[p]]+1, 1);
} else {
if (v > 0 && b[n+offset]) modify(n+offset-b[n+offset]+1, n+offset, -1);
offset -= v;
if (v < 0 && b[n+offset]) modify(n+offset-b[n+offset]+1, n+offset, 1);
}
printf("%d\n", query(offset+1, offset+n));
}
return 0;
}