Ryoku 的新年欢乐赛题解
Ryoku 的欢乐赛题解
A Ryoku 的探索
显然图为一个基环树。
拓扑排序找到环,对于环上的每一个点
#include <bits/stdc++.h>
typedef long long ll;
const int kMaxN = 2e6 + 5;
const int kInf = 0x3f3f3f3f;
struct Edge {
int to, nxt, prf, val;
} edges[kMaxN << 1];
int head[kMaxN], deg[kMaxN], ans[kMaxN], cnt = 0, n;
bool vis[kMaxN];
ll tot = 0;
void AddEdge(int u, int v, int w, int p) { edges[cnt] = (Edge) { v, head[u], p, w }; head[u] = cnt++; }
void Topsort(int cur) {
vis[cur] = true;
for(int i = head[cur]; ~i; i = edges[i].nxt) {
int v = edges[i].to;
if(vis[v]) continue;
--deg[v];
if(deg[v] == 1)
Topsort(v);
}
}
void SetAns(int cur, int w) {
ans[cur] = w;
for(int i = head[cur]; ~i; i = edges[i].nxt)
if(vis[edges[i].to] && !ans[edges[i].to])
SetAns(edges[i].to, w);
}
int main() {
memset(head, 0xff, sizeof(head));
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
int u, v, p, w;
scanf("%d%d%d%d", &u, &v, &w, &p);
AddEdge(u, v, w, p);AddEdge(v, u, w, p);
++deg[u]; ++deg[v];
tot += w;
}
for(int i = 1; i <= n; ++i) if(deg[i] == 1) Topsort(i);
for(int i = 1; i <= n; ++i)
if(!vis[i]) {
int minw, minprf = kInf;
for(int j = head[i]; ~j; j = edges[j].nxt)
if(!vis[edges[j].to] && edges[j].prf < minprf) {
minprf = edges[j].prf;
minw = edges[j].val;
}
SetAns(i, minw);
}
for(int i = 1; i <= n; ++i)
printf("%lld\n", tot - ans[i]);
return 0;
}
B Ryoku 与最初之人的笔记
考虑题目相当于求:
的对数。
注意到
观察二进制位,这个式子满足的条件是:对于每一位,
考虑最高位开始顶着上界,到第
对于比
利用乘法原理共
我实现的好像有一些细微差异。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll kMod = 1e9 + 7;
ll pow3[100];
int main() {
pow3[0] = 1;
for(int i = 1; i < 64; i++)
pow3[i] = pow3[i - 1] * 3 % kMod;
ll n, ans = 0; int k = 0;
cin >> n;
for(int i = 63; i >= 0; i--)
if((n >> i) & 1) {
ans = (ans + (1LL << k) % kMod * pow3[i] % kMod) % kMod;
k++;
}
ans = ((ans + (1LL << k) % kMod - n % kMod - 1) % kMod + kMod) % kMod;
cout << ans << endl;
return 0;
}
C Ryoku 的逆序对
(其实这道题本来想放 T1 的…不小心出难了就放这里了)
猜想到合法的序列
证明:
- 显然任意一个排列
A 对应唯一一个序列B 。 - 对于一个序列
B ,采取以下方式构造序列A :令a_i 为未出现的整数中第b_i + 1 小的。显然对于每一个B ,能构造出唯一一个A 。
证毕。
考虑合法的序列
这样,易证答案为:
显然,令所有
当然,本题也可以通过找规律得到答案。到这里再加上一些奇奇怪怪的构造可以获得
使用你喜欢的一个
#include <bits/stdc++.h>
typedef long long ll;
const int kMod = 1e9 + 7;
const int kMaxN = 1e6 + 5;
int b[kMaxN], seg[kMaxN << 3];
void Update(int cur) { seg[cur] = seg[cur << 1] + seg[cur << 1 | 1]; }
void Build(int cur, int l, int r) {
if(l < r) {
int mid = (l + r) >> 1;
Build(cur << 1, l, mid);
Build(cur << 1 | 1, mid + 1, r);
Update(cur);
} else seg[cur] = 1;
}
void Modify(int cur, int l, int r, int q, int k) {
if(l == r) seg[cur] = k;
else {
int mid = (l + r) >> 1;
if(q <= mid) Modify(cur << 1, l, mid, q, k);
if(q > mid) Modify(cur << 1 | 1, mid + 1, r, q, k);
Update(cur);
}
}
int QueryKth(int cur, int l, int r, int k) {
if(l == r) return l;
int siz = seg[cur << 1], mid = (l + r) >> 1;
if(k <= siz) return QueryKth(cur << 1, l, mid, k);
else return QueryKth(cur << 1 | 1, mid + 1, r, k - siz);
}
int main() {
int n; ll ans = 1;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", b + i);
if(b[i] > n - i) ans = 0;
else if(b[i] == -1) { ans = ans * (n - i + 1) % kMod; b[i] = 0; }
}
printf("%d\n", ans);
if(!ans) return 0;
Build(1, 1, n);
for(int i = 1; i <= n; ++i) {
int x = QueryKth(1, 1, n, b[i] + 1);
printf("%d ", x);
Modify(1, 1, n, x, 0);
}
return 0;
}
D 爱学习的 Ryoku
upd:这题我已经把我自己卡掉了,可以
然而,大部分julao貌似都不是这个做法,他们具体是怎么做的我也不知道。但是精度应该比我强。
而且这题好像可以用奇奇怪怪的做法水过去
首先要给
令
考虑
整理得:
注意到期望具有线性性,设
考虑如何分别求出这两个期望。
设:(代表以
易得(
预处理出组合数,用这个去递推就可以了。
时间复杂度
#include <bits/stdc++.h>
typedef long long ll;
typedef long double ff;
const int kMaxN = 2e5 + 5;
const int kMaxTerm = 9;
const int kMaxT = kMaxTerm + 5;
ff m[kMaxT][kMaxT];
void Div(int i, ff v) { for(int j = 0; j <= kMaxTerm + 1; ++j) m[i][j] /= v; }
void Rem(int i0, int i1, ff t) { for(int j = 0; j <= kMaxTerm + 1; ++j) m[i0][j] -= t * m[i1][j]; }
int n, w[kMaxN]; ll C[kMaxT][kMaxT];
double a, b, p[kMaxN], pt[kMaxT] = {20, 15, 10, 8, 7, 5, 4, 3, 2, 1};
ff c[kMaxN], W[kMaxN][kMaxT], P[kMaxN][kMaxT], ans[kMaxN];
void Init() {
for(int i = 0; i <= kMaxTerm; ++i) {
long double x = pt[i];
m[i][0] = 1;
for(int j = 1; j <= kMaxTerm; ++j) m[i][j] = m[i][j - 1] * x;
m[i][kMaxTerm + 1] = pow((ff)a, (ff)b * x);
}
for(int i = 0; i <= kMaxTerm; ++i) {
Div(i, m[i][i]);
for(int j = 0; j <= kMaxTerm; ++j)
if(j != i) Rem(j, i, m[j][i]);
}
for(int i = 0; i <= kMaxTerm; ++i) c[i] = m[i][kMaxTerm + 1];
C[0][0] = 1;
for(int i = 1; i <= kMaxTerm; ++i) {
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; ++j)
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
void Solve() {
for(int i = 1; i <= n; ++i) {
P[i][0] = 1;
W[i][0] = (W[i - 1][0] + w[i]) * p[i];
}
for(int q = 1; q <= kMaxTerm; ++q) {
W[1][q] = 0; P[1][q] = p[1];
for(int r = 2; r <= n; ++r) {
ff tmp1 = 0, tmp2 = 0;
for(int t = 0; t <= q; ++t) tmp1 += C[q][t] * W[r - 1][t], tmp2 += C[q][t] * P[r - 1][t];
P[r][q] = p[r] * tmp2;
W[r][q] = p[r] * (tmp1 + P[r - 1][q] * w[r]);
}
}
ans[1] = p[1] * w[1];
for(int r = 2; r <= n; ++r) {
for(int j = 0; j <= kMaxTerm; ++j) {
ff tmp = 0;
for(int q = 0; q < j; ++q)
tmp += C[j][q] * W[r - 1][q];
tmp += P[r - 1][j] * w[r];
ans[r] += tmp * c[j];
}
ans[r] = p[r] * ans[r] + ans[r - 1];
}
}
int main() {
scanf("%d%lf%lf", &n, &a, &b);
for(int i = 1; i <= n; ++i) scanf("%d", w + i);
for(int i = 1; i <= n; ++i) scanf("%lf", p + i);
Init(); Solve();
printf("%.4Lf", ans[n]);
return 0;
}