ICPC 2021 昆明
补题计划 CLE,
C - Cup of Water
prob : 在0-V内随机取数灌满1升水的期望操作次数
idea1:
首先将题给的“在0-V内随机取数灌满1升水”改为“在0-1内随机取数灌满 1 V \frac{1}{V} V1升水”
这样方程是统一的,否则在V>1和<1的情况下形式不同
def f ( x ) : = f(x) := f(x):= 已有x升水的情况下,灌满1升水的期望步数
f ( x ) = 1 + ∫ m a x ( 0 , x − 1 ) x f ( x − t ) d t f(x) = 1 + \int_{max(0,x-1)}^{x}f(x - t)\mathrm{d}t f(x)=1+∫max(0,x−1)xf(x−t)dt
分成若干小段然后求体积和
btw >1的值是 e x p ( 1 x ) exp(\frac{1}{x}) exp(x1),不会证但感觉非常优美(
有轻微调参
code :
#include <bits/stdc++.h>
using namespace std;
const int N = 7e5;
//typedef long double lb;
double dp[N * 20 + 10], pre[N * 20 + 10];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T;
cin >> T;
for (int i = 1; i < N * 20 + 5; ++i) {
dp[i] = 1 + (double)(pre[i - 1] - pre[max(i - N, 0)]) / (double)N;
pre[i] = pre[i - 1] + dp[i];
}
while (T--) {
double V;
cin >> V;
cout << fixed << setprecision(10) << dp[(int)(1.0 / V * (N)) + 1] << endl;
}
return 0;
}
C题 官方题解做法和高维体积容斥(?)思路待
推荐一下zx宝贝的C题题解
L - Light of Stars
prob. : 二维平面上n个星星,每个星星有k个照的角度区间,(区间不重合,且所有星星的角度是一样的),问每个星星被多少个星星照到
每个角度分开做,做k次
坐标一步步变换成图4的样子,二维偏序计数
(因为统计每个星星被照到几次,将照的角度变成右上角90°的范围,他的左下角(即x<this.x & y <this.y 的范围内的点就是能照到他的点))
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const double PI = acos(-1.0);
const double eps = 1e-9;
//#define double long double
struct node {
double x, y;
};
struct note {
double x, y;
int id;
};
note tmp[N << 2];
node p[N];
int ans[N];
int n;
int tree[N];
int lowbit(int x) {
return x & (-x);
}
void update(int i, int x) {
while (i <= n) {
tree[i] += x;
i += lowbit(i);
}
}
int query(int i) {
int res = 0;
while (i) {
res += tree[i];
i -= lowbit(i);
}
return res;
}
note rotate(const note &a, const double &ang) {
double x = cos(ang) * a.x - sin(ang) * a.y;
double y = sin(ang) * a.x + cos(ang) * a.y;
return {x, y, a.id};
}
int cmp(double a, double b) {
if (abs(a - b) < eps) return 0;
if (a > b) return 1;
return -1;
}
void solve(int l, int r) {
memset(tree, 0, sizeof tree);
for (int i = 1; i <= n; ++i) {
tmp[i].x = p[i].x;
tmp[i].y = p[i].y;
tmp[i].id = i;
}
vector<double> vec;
for (int i = 1; i <= n; ++i) {
tmp[i] = rotate(tmp[i], (double) (l + r)* PI / 360.0);
tmp[i].x = tan((double) (r - l) * PI / 360.0) * tmp[i].x;
tmp[i] = rotate(tmp[i], (double) 45 * PI / 180.0);
vec.push_back(tmp[i].y);
}
sort(tmp + 1, tmp + n + 1, [&](note a, note b) {
if (cmp(a.x, b.x) != 0) return a.x < b.x;
return a.y < b.y;
});
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end(), [&](double x, double y) {
return abs(x - y) < eps;
}), vec.end());
for (int i = 1; i <= n; ++i) {
int y = lower_bound(vec.begin(), vec.end(), tmp[i].y + eps) - vec.begin() + 1;
ans[tmp[i].id] += query(y);
update(y, 1);
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int k;
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> p[i].x >> p[i].y;
}
for (int i = 0; i < k; ++i) {
int l, r;
cin >> l >> r;
if (l > r) swap(l, r);
solve(l, r);
// for (int j = 1; j <= n; ++j) {
// cerr << ans[j] << " ";
// }
// cerr << endl;
}
for (int i = 1; i <= n; ++i) {
cout << ans[i] << " ";
}
return 0;
}
E - Easy String Problem
prob. : 给一个字符串,若干次询问,每次问删去包含 [ L , R ] [L, R] [L,R]区间的子串形成的不同的字符串的数量
idea :
刚开始一直觉得如果有两个区间的字符串是一样的,这个串包含了 O ( l e n 2 ) O(len^2) O(len2)个相同的区间所以贡献是 O ( l e n 2 ) O(len^2) O(len2),然后觉得非常不可做
可他有 O ( l e n ) O(len) O(len) 种长度啊,
如果删去 [ l , r ] [l, r] [l,r] 区间的字符串与删去 [ l + k , r + k ] [l + k, r + k] [l+k,r+k] 区间的字符串相等,则对于 i ∈ [ 0 , k ] i \in [0, k] i∈[0,k] 删去 [ l + i , r + i ] [l + i, r + i] [l+i,r+i] 区间得到的字符串都相等
题目转化成求满足 [ l , r ] ≠ [ l − 1 , r − 1 ] [l,r ] \not =[l - 1, r - 1] [l,r]=[l−1,r−1]的数量
即 L × ( n − R + 1 ) − c n t { [ l , r ] = [ l − 1 , r − 1 ] } L\times(n -R+1) - cnt\{[l,r ] =[l - 1, r - 1]\} L×(n−R+1)−cnt{[l,r]=[l−1,r−1]}
[ l , r ] = [ l − 1 , r − 1 ] [l,r ] =[l - 1, r - 1] [l,r]=[l−1,r−1] 又可以转化成求两边相同的字符对数
莫队
code :
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5 + 10;
struct node {
ll l, r;
ll id;
};
ll len;
ll a[N], cntLeft[N], cntRight[N], ans[N];
node ques[N];
ll n;
ll getNum(ll l) {
return l / len;
}
void add(ll x, ll &res, ll op) {
if (op == 1) {
res -= cntLeft[x];
cntRight[x]--;
} else {
res -= cntRight[x];
cntLeft[x]--;
}
}
void del(ll x, ll &res, ll op) {
if (op == 1) {
res += cntLeft[x];
cntRight[x]++;
} else {
res += cntRight[x];
cntLeft[x]++;
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (ll i = 1; i <= n; ++i) {
cin >> a[i];
cntRight[a[i]]++;
}
ll q;
cin >> q;
for (ll i = 1; i <= q; ++i) {
cin >> ques[i].l >> ques[i].r;
ques[i].id = i;
}
len = sqrt((double) n * n / q);
sort(ques + 1, ques + q + 1, [&](node a, node b) {
if (getNum(a.l) != getNum(b.l)) return a.l < b.l;
return a.r < b.r;
});
ll i = 0, j = 0, res = 0;
for (ll k = 1; k <= q; ++k) {
ll id = ques[k].id, l = ques[k].l, r = ques[k].r;
while (j < r) add(a[++j], res, 1);
while (j > r) del(a[j--], res, 1);
while (i < l) del(a[i++], res, 0);
while (i > l) add(a[--i], res, 0);
ans[id] = l * (n - r + 1) - res;
}
for (ll k = 1; k <= q; ++k) cout << ans[k] << endl;
return 0;
}