SDOI 2017 Round1 解题报告
Day 1
T1 数字表格
题目大意
· 求\(\prod\limits_{i=1}^n\prod\limits_{j=1}^mFibonacci(\gcd(i,j))\),\(T\leq1000\),\(n,m\leq10^6\)
思路
· 一言不合化式子(不失一般性地假设\(n<m\))$$\begin{aligned}
ans&=\prod_{i=1}n\prod_{j=1}mf((i,j))\
&=\prod_{d=1}nf(d){\sum\limits_{i=1}n\sum\limits_{j=1}m[(i,j)=d]}\
&=\prod_{d=1}nf(d){\sum\limits_{k=1}^{\frac nd}\mu(k)\left\lfloor\frac n{dk}\right\rfloor\left\lfloor\frac m{dk}\right\rfloor}
\end{aligned}$$
· 到这里直接\(O\left(Tn^{\frac 34}\right)\)暴力计算答案,常数压的好就可以通过此题。
· 更神奇的:$$\prod_{d=1}nf(d){\sum\limits_{k=1}^{\frac nd}\mu(k)\left\lfloor\frac n{dk}\right\rfloor\left\lfloor\frac m{dk}\right\rfloor}=\prod_{T=1}n\left(\prod_{d|T}f(d){\mu\left(\frac Td\right)}\right)^{\left\lfloor\frac nT\right\rfloor\left\lfloor\frac mT\right\rfloor}$$
· 我们设\(g(n)=\prod\limits_{d|n}f(d)^{\mu\left(\frac nd\right)}\)
· 很类似莫比乌斯反演,\(f(n)=\prod\limits_{d|n}g(n)\),这个结论没什么意义,不过可以从另一个角度来做这道题。
· 如何预处理\(g(i)\)呢?\(g(i)\)并不是积性函数,但我们可以枚举所有斐波那契数,然后再枚举它们的倍数来计算对\(g(i)\)的贡献,时间复杂度\(O(n\ln n)\)。
· 预处理完成后\(O\left(\left(\sqrt n+\sqrt m\right)\log n\right)\)回答单次询问。
· 时间复杂度\(O\left(n\ln n+T\left(\sqrt n+\sqrt m\right)\log n\right)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1000003;
const int p = 1000000007;
bool notp[N];
int mu[N], prime[N], num = 0, g[N], nig[N];
int ipow(int a, int b) {
int r = 1, w = a;
while (b) {
if (b & 1) r = 1ll * r * w % p;
w = 1ll * w * w % p;
b >>= 1;
}
return r;
}
void Euler_shai() {
mu[1] = 1;
for (int i = 2; i <= 1000000; ++i) {
if (!notp[i]) {
prime[++num] = i;
mu[i] = -1;
}
for (int j = 1, cj; j <= num && (cj = prime[j] * i) <= 1000000; ++j) {
notp[cj] = true;
if (i % prime[j] == 0) {
mu[cj] = 0;
break;
}
mu[cj] = -mu[i];
}
}
for (int i = 0; i <= 1000000; ++i) g[i] = nig[i] = 1;
}
int Fib[N], niFib[N];
int solve(int n, int m) {
int ret = 1;
for (int ri, T = 1; T <= n; ++T) {
ri = min(n / (n / T), m / (m / T));
ret = 1ll * ret * ipow(1ll * g[ri] * nig[T - 1] % p, 1ll * (n / T) * (m / T) % (p - 1)) % p;
T = ri;
}
return ret;
}
int main() {
freopen("product.in", "r", stdin);
freopen("product.out", "w", stdout);
Euler_shai();
Fib[0] = 0; Fib[1] = 1; niFib[1] = niFib[0] = 1;
for (int i = 2; i <= 1000000; ++i) {
Fib[i] = (Fib[i - 1] + Fib[i - 2]) % p;
niFib[i] = ipow(Fib[i], p - 2);
}
for (int i = 1; i <= 1000000; ++i)
for (int j = i; j <= 1000000; j += i)
if (mu[j / i] == 1) {
g[j] = 1ll * g[j] * Fib[i] % p;
nig[j] = 1ll * nig[j] * niFib[i] % p;
} else if (mu[j / i] == -1) {
g[j] = 1ll * g[j] * niFib[i] % p;
nig[j] = 1ll * nig[j] * Fib[i] % p;
}
for (int i = 2; i <= 1000000; ++i) {
g[i] = 1ll * g[i] * g[i - 1] % p;
nig[i] = 1ll * nig[i] * nig[i - 1] % p;
}
int T, n, m; scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
if (n > m) n ^= m ^= n ^= m;
printf("%d\n", solve(n, m));
}
return 0;
}
T2 树点涂色
题目大意
· 根为1的有根树上初始每个节点都有不同颜色,一条路径的权值为这条路径上点的不同颜色数。
· 修改:把x到根这条链上所有点都涂上一种之前从没出现过的颜色。
· 查询:链的权值和子树内一点到根的路径的权值的最大值。
· \(n\leq10^5,m\leq10^5\)
思路
· 把具有相同颜色的链看成重链,那么重链上相邻的点只可能是父子关系,不可能是兄弟关系。
· 线段树维护dfs序,每个节点维护这个区间内所有点到根路径的权值的最大值,链的权值可以两个端点和lca差分一下求出,子树内一点到根的权值的最大值直接在线段树上查询子树的dfs序区间内的最大值。
· 修改操作类似lct的access操作,每次修改x点,相当于把x到根的这条路径变成重链,同时要相应的断掉以前的一些重链。在重链和轻链变换的时候顺便在线段树上修改即可。
· 时间复杂度\(O(m\log^2n)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100003;
int n, deep[N], pos[N];
void check_max(int &a, int b) {if (b > a) a = b;}
namespace SegmentTree {
int ma[N << 2], lazy[N << 2];
void BuildTree(int rt, int l, int r) {
if (l == r) {ma[rt] = deep[pos[l]]; return;}
int mid = (l + r) >> 1;
BuildTree(rt << 1, l, mid);
BuildTree(rt << 1 | 1, mid + 1, r);
ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
}
void pushdown(int rt) {
if (lazy[rt]) {
int num = lazy[rt];
lazy[rt << 1] += num;
ma[rt << 1] += num;
lazy[rt << 1 | 1] += num;
ma[rt << 1 | 1] += num;
lazy[rt] = 0;
}
}
void add(int rt, int l, int r, int L, int R, int num) {
if (L <= l && r <= R) {lazy[rt] += num; ma[rt] += num; return;}
int mid = (l + r) >> 1;
pushdown(rt);
if (L <= mid) add(rt << 1, l, mid, L, R, num);
if (R > mid) add(rt << 1 | 1, mid + 1, r, L, R, num);
ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
}
int query(int rt, int l, int r, int L, int R) {
if (L <= l && r <= R) return ma[rt];
int mid = (l + r) >> 1, s = 0;
pushdown(rt);
if (L <= mid) check_max(s, query(rt << 1, l, mid, L, R));
if (R > mid) check_max(s, query(rt << 1 | 1, mid + 1, r, L, R));
return s;
}
}
struct Enode {int nxt, to;} E[N << 1];
int cnt = 0, point[N], st[N], top;
void ins(int u, int v) {E[++cnt] = (Enode) {point[u], v}; point[u] = cnt;}
int m, L[N], R[N], dfsID = 0, sz[N], fath[N];
bool used[N];
void dfs() {
st[top = 1] = 1; deep[1] = 1;
while (top) {
int u = st[top];
if (!used[u]) {
L[u] = ++dfsID; pos[dfsID] = u;
for (int i = point[u]; i; i = E[i].nxt)
if (E[i].to != fath[u]) {
st[++top] = E[i].to;
fath[E[i].to] = u;
deep[E[i].to] = deep[u] + 1;
}
used[u] = true;
} else {
for (int i = point[u]; i; i = E[i].nxt)
if (E[i].to != fath[u])
sz[u] += sz[E[i].to];
R[u] = L[u] + sz[u];
++sz[u];
--top;
}
}
}
namespace LCT {
struct node *null;
struct node {
node *ch[2], *fa; int id, mn;
void setc(node *r, int c) {ch[c] = r; if (r != null) r->fa = this;}
bool pl() {return fa->ch[1] == this;}
bool check() {return fa == null || (fa->ch[0] != this && fa->ch[1] != this);}
void count() {
mn = id;
if (ch[0] != null) if (L[ch[0]->mn] < L[mn]) mn = ch[0]->mn;
if (ch[1] != null) if (L[ch[1]->mn] < L[mn]) mn = ch[1]->mn;
}
} pool[N];
void init() {
null = &pool[0]; null->ch[0] = null->ch[1] = null->fa = null;
node *t;
for (int i = 1; i <= n; ++i) {
t = pool + i; t->id = t->mn = i;
t->ch[0] = t->ch[1] = null;
t->fa = &pool[fath[i]];
}
}
void rotate(node *r) {
node *f = r->fa;
int c = r->pl();
if (f->check()) r->fa = f->fa;
else f->fa->setc(r, f->pl());
f->setc(r->ch[c ^ 1], c);
r->setc(f, c ^ 1);
f->count();
}
void splay(node *r) {
for (; !r->check(); rotate(r))
if (!r->fa->check()) rotate(r->pl() == r->fa->pl() ? r->fa : r);
r->count();
}
void access(node *r) {
node *y = null;
while (r != null) {
splay(r);
if (r->ch[1] != null) SegmentTree::add(1, 1, n, L[r->ch[1]->mn], R[r->ch[1]->mn], 1);
r->ch[1] = y; r->count();
if (y != null) SegmentTree::add(1, 1, n, L[y->mn], R[y->mn], -1);
y = r; r = r->fa;
}
}
}
int F[N][18];
int work(int x, int y) {
if (deep[x] < deep[y]) x ^= y ^= x ^= y;
int xx = x, yy = y;
for (int i = 17; i >= 0; --i)
if (deep[F[x][i]] >= deep[y])
x = F[x][i];
if (x == y) return SegmentTree::query(1, 1, n, L[xx], L[xx]) - SegmentTree::query(1, 1, n, L[x], L[x]) + 1;
for (int i = 17; i >= 0; --i)
if (F[x][i] != F[y][i])
x = F[x][i], y = F[y][i];
int numx, numy, numz;
int ret = SegmentTree::query(1, 1, n, L[xx], L[xx]) - (numx = SegmentTree::query(1, 1, n, L[x], L[x])) + 1
+ SegmentTree::query(1, 1, n, L[yy], L[yy]) - (numy = SegmentTree::query(1, 1, n, L[y], L[y])) + 1;
numz = SegmentTree::query(1, 1, n, L[fath[x]], L[fath[x]]);
if (numz != numx && numz != numy) ++ret;
return ret;
}
int main() {
freopen("paint.in", "r", stdin);
freopen("paint.out", "w", stdout);
scanf("%d%d", &n, &m);
int u, v;
for (int i = 1; i < n; ++i) {
scanf("%d%d", &u, &v);
ins(u, v); ins(v, u);
}
dfs();
SegmentTree::BuildTree(1, 1, n);
LCT::init();
for (int i = 1; i <= n; ++i) F[i][0] = fath[i];
for (int j = 1; j <= 17; ++j)
for (int i = 1; i <= n; ++i)
F[i][j] = F[F[i][j - 1]][j - 1];
int x, y, op;
while (m--) {
scanf("%d%d", &op, &x);
if (op == 2) scanf("%d", &y);
switch (op) {
case 1:
LCT::access(&LCT::pool[x]);
break;
case 2:
printf("%d\n", work(x, y));
break;
case 3:
printf("%d\n", SegmentTree::query(1, 1, n, L[x], R[x]));
break;
}
// for (int i = 1; i <= n; ++i) printf("%d ", SegmentTree::query(1, 1, n, L[i], L[i])); puts("");
}
return 0;
}
T3 序列计数
题目大意
· 长度为\(n\)的序列,满足序列中的数都是不大于\(m\)的正整数,\(n\)个数的和是\(p\)的倍数,而且这\(n\)个数中至少有一个是质数。
· 求满足条件的序列的个数。
· \(1\leq n\leq 10^9,1\leq m \leq 2\times 10^7,1\leq p \leq 100\)
思路
· 容斥一下,在满足前两个条件的情况下,统计\(n\)个数无质数限制的序列个数再减去\(n\)个数都不是质数的序列个数。
· 设\(f(i,j)\)表示长度为\(i\),和模\(p\)为\(j\)的序列个数。
· 提前预处理模\(p\)为\(j\)有多少个数,即\(f(1,j)\)。欧拉筛求出\(m\)以内的质数。
· \(f(i,j)=\sum\limits_{k,l}[(k+l)\mod p=j]f(i-1,k)\times f(1,l)\)
· dp可以用倍增优化,时间复杂度\(O\left(m+p^2\log n\right)\)。
· 当然矩乘也是可以的。
#include<cstdio>
#include<bitset>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 20000003;
const int mo = 20170408;
int f[103], g[103], F[103], n, m, p;
int nump = 0, prime[1270608];
bitset <N> notp;
void Euler_shai() {
notp[1] = 1;
for (int i = 2; i <= m; ++i) {
if (!notp[i]) prime[++nump] = i;
for (int j = 1; j <= nump && prime[j] * i <= m; ++j) {
notp[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
}
int main() {
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
scanf("%d%d%d", &n, &m, &p);
int num = n, tot = 0;
while (num) {num >>= 1; ++tot;}
for (int i = 1; i <= m; ++i) ++f[i % p];
for (int i = 0; i < p; ++i) F[i] = (f[i] %= mo);
for (int i = tot - 2; i >= 0; --i) {
memset(g, 0, p << 2);
for (int j = 0; j < p; ++j)
for (int k = 0; k < p; ++k)
(g[(j + k) % p] += 1ll * f[j] * f[k] % mo) %= mo;
memcpy(f, g, p << 2);
if ((n >> i) & 1) {
memset(g, 0, p << 2);
for (int j = 0; j < p; ++j)
for (int k = 0; k < p; ++k)
(g[(j + k) % p] += 1ll * f[j] * F[k] % mo) %= mo;
memcpy(f, g, p << 2);
}
}
int ans = f[0];
Euler_shai();
memset(f, 0, p << 2); memset(F, 0, p << 2);
for (int i = 1; i <= m; ++i) if (notp[i]) ++f[i % p];
for (int i = 0; i < p; ++i) F[i] = (f[i] %= mo);
for (int i = tot - 2; i >= 0; --i) {
memset(g, 0, p << 2);
for (int j = 0; j < p; ++j)
for (int k = 0; k < p; ++k)
(g[(j + k) % p] += 1ll * f[j] * f[k] % mo) %= mo;
memcpy(f, g, p << 2);
if ((n >> i) & 1) {
memset(g, 0, p << 2);
for (int j = 0; j < p; ++j)
for (int k = 0; k < p; ++k)
(g[(j + k) % p] += 1ll * f[j] * F[k] % mo) %= mo;
memcpy(f, g, p << 2);
}
}
printf("%d\n", (ans - f[0] + mo) % mo);
return 0;
}
Day 2
T1 新生舞会
题目大意
· \(n\)个男生和\(n\)个女生组成舞伴,第\(i\)个男生和第\(j\)个女生组成舞伴会产生\(a_{ij}\)的喜悦程度和\(b_{ij}\)的不协调程度。
· 有\(n\)个喜悦程度\(a_1',a_2',\dots a_n'\)和不协调程度\(b_1',b_2',\dots b_n'\),最大化\(C=\frac{a_1'+a_2'+\dots+a_n'}{b_1'+b_2'+\dots+b_n'}\)
· \(1\leq n\leq 100,1\leq a_{ij},b_{ij}\leq 10^4\),保留6位小数。
思路
· 分数规划,把分母乘到左边, 左边再移向到右边,二分\(C\)的值,通过移项后的式子的最大值来判断\(C\)偏大还是偏小。
· 移项后的式子的最大值可以用最大费用最大流来计算。
· 用数组存边比结构体快一倍!!!
· 把实数乘\(10^7\)变成整数可以减小常数。
· 时间复杂度\(O\left(n^2\sqrt n\log 10^{12}\right)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int in() {
int k = 0; char c = getchar();
for (; c < '0' || c > '9'; c = getchar());
for (; c >= '0' && c <= '9'; c = getchar())
k = k * 10 + (c ^ 48);
return k;
}
const int N = 103;
int b[N][N], n, S, T;
ll a[N][N], w[N * N * 3];
int nxt[N * N * 3], to[N * N * 3], c[N * N * 3], cnt, point[N << 1];
void ins(int u, int v, int C, ll W) {
++cnt; nxt[cnt] = point[u]; to[cnt] = v; c[cnt] = C; w[cnt] = W; point[u] = cnt;
++cnt; nxt[cnt] = point[v]; to[cnt] = u; c[cnt] = 0; w[cnt] = -W; point[v] = cnt;
}
bool inq[N << 1];
ll dis[N << 1];
int qu[100000], pre[N << 1];
bool spfa() {
memset(dis, 0xc0, (T + 1) << 3);
int p = 0, q = 1, v;
ll t;
qu[1] = S; dis[S] = 0; inq[S] = true;
while (p != q) {
++p; if (p == 100000) p = 0;
int u = qu[p]; inq[u] = false;
for (int i = point[u]; i; i = nxt[i])
if (c[i] && (t = dis[u] + w[i]) > dis[v = to[i]]) {
pre[v] = i;
dis[v] = t;
if (!inq[v]) {
++q; if (q == 100000) q = 0;
qu[q] = v;
inq[v] = true;
}
}
}
return dis[T] != dis[0];
}
ll mcmf(ll C) {
cnt = 1; memset(point, 0, (T + 1) << 2);
for (int i = 1; i <= n; ++i) {
ins(S, i, 1, 0), ins(i + n, T, 1, 0);
ll *ta = a[i]; int *tb = b[i];
for (int j = 1; j <= n; ++j) {
++ta; ++tb;
ins(i, j + n, 1, *ta - C * *tb);
}
}
ll f = 0;
while (spfa()) {
int u = T, e;
for (u = T, e = pre[u]; u != S; e = pre[u = to[e ^ 1]]) --c[e], ++c[e ^ 1];
f += dis[T];
}
return f;
}
int main() {
freopen("ball.in", "r", stdin);
freopen("ball.out", "w", stdout);
n = in();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
a[i][j] = in(), a[i][j] *= 10000000ll;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
b[i][j] = in();
S = (n << 1) + 1; T = S + 1;
ll left, right, s1 = 0, s2 = 0;
for (int i = 1; i <= n; ++i)
s1 += a[i][i], s2 += b[i][i];
left = s1 / s2 + 1;
s1 = 0; s2 = 0;
ll mx;
for (int i = 1; i <= n; ++i) {
mx = 0;
for (int j = 1; j <= n; ++j)
if (a[i][j] > mx)
mx = a[i][j];
s1 += mx;
mx = 0x7fffffff;
for (int j = 1; j <= n; ++j)
if (b[i][j] < mx)
mx = b[i][j];
s2 += mx;
}
right = s1 / s2;
ll mid;
while (left < right) {
mid = (left + right + 1) >> 1;
if (mcmf(mid) >= 0) left = mid;
else right = mid - 1;
}
if (left % 10 >= 5) left += 10; left /= 10;
printf("%I64d.%06I64d\n", left / 1000000, left % 1000000);
return 0;
}
T2 硬币游戏
题目大意
· \(n\)个同学每个同学猜一个长度为\(m\)的HT序列(H表示正面朝上,T表示反面朝上)
· 不断地扔硬币,组成一个HT序列。当某个同学的猜的序列突然出现在扔出来的硬币序列中,则停止扔硬币并且这个同学获胜。
· 问每个同学获胜的概率是多少。
· \(1\leq n,m\leq 300\)
思路
· 这道题我想了一整天,最后问了zyf2000才知道怎么做。
· 某个同学猜的序列在硬币序列中出现的位置肯定是最后。
· 首先设\(f(i,j)\)表示长度为\(i\)的硬币序列,只有第\(i\)个位置和\(m\)个序列中某一个序列匹配,并且这个匹配的序列是第\(j\)个序列的概率。
· 那么第\(i\)个同学获胜的概率就是\(\sum\limits_{j\geq 1}f(j,i)\)
· 设\(N\)表示没有扔出来和同学猜的序列匹配的硬币序列的概率,这个\(N\)有点抽象,更具体的:\(N=\sum\limits_{j\geq 1}\left(1-\sum\limits_{k=1}^nf(j,k)\right)\)
· 然后假设A同学猜的序列是TTH,B同学猜的序列是HTT,那么可以构造一个以TTH结尾的硬币序列,并且1到序列长度-3的所有位置都不是能匹配A或B的位置,所以匹配位置只可能是序列长度-2
,序列长度-1或序列长度(即最后一个位置)
· 这样就有一个等式了:P(NTTH)=P(A)+P(BTH)+P(BH)
· 很明显吧,如果一个序列的前缀是另一个序列的后缀,那么就会产生贡献。
· 所有同学获胜的概率和为1,\(n+1\)个变元,\(n+1\)个等式,高斯消元即可。
· 时间复杂度\(O(n^3)\)。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 703;
int cnt;
double a[N][N], f[N];
void gauss() {
for (int i = 1; i <= cnt; ++i) {
int tmp = i;
for (int j = i + 1; j <= cnt; ++j)
if (fabs(a[j][i]) > fabs(a[tmp][i]))
tmp = j;
if (tmp != i)
for (int j = i; j <= cnt + 1; ++j)
swap(a[i][j], a[tmp][j]);
for (int j = i + 1; j <= cnt; ++j)
if (a[j][i] != 0) {
double k = a[j][i] / a[i][i];
for (int t = i; t <= cnt + 1; ++t)
a[j][t] -= a[i][t] * k;
}
}
for (int i = cnt; i >= 1; --i) {
for (int j = i + 1; j <= cnt; ++j)
a[i][cnt + 1] -= a[i][j] * f[j];
f[i] = a[i][cnt + 1] / a[i][i];
}
}
int end[N];
double fac[N];
int n, m, Acnt = 1, ch[N * N][2], fail[N * N], qu[N * N], deep[N * N], to[N * N];
char s[N];
void insert(int id) {
int tmp = 1;
for (int i = 1; i <= m; ++i) {
int v = s[i] == 'H' ? 0 : 1;
if (ch[tmp][v] == 0) ch[tmp][v] = ++Acnt;
tmp = ch[tmp][v];
}
end[id] = tmp; to[tmp] = id;
}
int dfsID = 0, L[N * N], R[N * N], dfs_a[N * N];
void dfs(int x) {
L[x] = ++dfsID;
if (to[x])
dfs_a[dfsID] = to[x];
else {
if (ch[x][0]) dfs(ch[x][0]);
if (ch[x][1]) dfs(ch[x][1]);
}
R[x] = dfsID;
}
void BuildFail() {
int p = 0, q = 1; qu[1] = 1;
while (p != q) {
int u = qu[++p];
for (int i = 0; i <= 1; ++i)
if (ch[u][i]) {
int p = fail[u];
while (p && ch[p][i] == 0) p = fail[p];
fail[ch[u][i]] = p ? ch[p][i] : 1;
deep[ch[u][i]] = deep[u] + 1;
qu[++q] = ch[u][i];
}
}
dfs(1);
}
void Jump(int id) {
int p = fail[end[id]];
while (p != 1) {
for (int i = L[p]; i <= R[p]; ++i)
if (dfs_a[i])
a[dfs_a[i]][id] += fac[m - deep[p]];
p = fail[p];
}
}
int main() {
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
insert(i);
}
fac[0] = 1;
for (int i = 1; i <= m; ++i) fac[i] = fac[i - 1] / 2;
BuildFail();
cnt = n + 1;
for (int i = 1; i <= n; ++i) {
a[i][i] = 1; a[i][cnt] = -1;
Jump(i);
}
for (int i = 1; i <= n; ++i) a[cnt][i] = 1;
a[cnt][cnt + 1] = 1;
gauss();
for (int i = 1; i <= n; ++i) printf("%.10lf\n", f[i]);
return 0;
}
T3 相关分析
题目大意
· \(n\)个二元组\((x,y)\)构成一个序列。
· 修改\([L,R],S,T\):区间内所有二元组的\(x\)都加上\(S\),\(y\)都加上\(T\)。
· 修改\([L,R],S,T\):区间内所有二元组的\(x\)都变为\(S+i\),\(y\)都变为\(T+i\),\(i\)为二元组的下标。
· 查询\([L,R]\):计算\(\frac{\sum\limits_{i=L}^R(x_i-\bar x)(y_i-\bar y)}{\sum\limits_{i=L}^R(x_i-\bar x)^2}\)
· \(1\leq n,m\leq10^5,1\leq L\leq R\leq n,0\leq|S|,|T|\leq10^5,0\leq|x_i|,|y_i|\leq10^5\)
思路
· 线段树维护4个信息:\(\sum x_i,\sum y_i,\sum x_i^2,\sum x_iy_i\)
· 把要计算的式子全拆开,所需要的信息就是上面的4个信息。
· 区间加:\(\sum (x_i+S)^2=\sum(x_i^2+2Sx_i+S^2),\sum (x_i+S)(y_i+T)=\sum(x_iy_i+Tx_i+Sy_i+ST)\)
· 区间覆盖:\(\sum x_i^2\)可以用平方和公式\(\frac {n(n+1)(2n+1)}6\)计算(直接预处理平方和可以减小常数),\(\sum x_iy_i\)同理。
· 维护一个区间加标记和区间覆盖标记就可以了。
· 时间复杂度\(O(m\log n)\)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int in() {
int k = 0, fh = 1; char c = getchar();
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') fh = -1;
for (; c >= '0' && c <= '9'; c = getchar())
k = k * 10 + (c ^ 48);
return k * fh;
}
const int N = 100003;
int n, m, len;
double x[N], y[N], xl, yl;
struct PP {
double sumx, sumy, sumxx, sumxy;
PP (double _sumx = 0, double _sumy = 0, double _sumxx = 0, double _sumxy = 0)
: sumx(_sumx), sumy(_sumy), sumxx(_sumxx), sumxy(_sumxy) {}
PP operator + (const PP &A) const {
return PP(sumx + A.sumx, sumy + A.sumy, sumxx + A.sumxx, sumxy + A.sumxy);
}
};
namespace ST {
double sum_x[N << 2], sum_y[N << 2], sum_xy[N << 2], sum_xx[N << 2], lazy_x[N << 2], lazy_y[N << 2];
double cover_x[N << 2], cover_y[N << 2];
bool cover_mark[N << 2];
ll getsum(int l, int r) {
return (1ll * r * (r + 1) * ((r << 1) + 1) - 1ll * (l - 1) * l * (((l - 1) << 1) + 1)) / 6;
}
void coverit(int rt, int l, int r, const double &S, const double &T) {
lazy_x[rt] = lazy_y[rt] = 0;
cover_x[rt] = S; cover_y[rt] = T; cover_mark[rt] = true;
len = r - l + 1;
sum_x[rt] = (S + l + S + r) * len / 2;
sum_y[rt] = (T + l + T + r) * len / 2;
sum_xx[rt] = getsum(S + l, S + r);
sum_xy[rt] = S * T * len + (S + T) * (1ll * (l + r) * len / 2) + getsum(l, r);
}
double Slen, Tlen;
void update(int rt, int l, int r, const double &S, const double &T) {
if (cover_mark[rt]) {
if (l != r) {
int mid = (l + r) >> 1;
coverit(rt << 1, l, mid, cover_x[rt], cover_y[rt]);
coverit(rt << 1 | 1, mid + 1, r, cover_x[rt], cover_y[rt]);
}
cover_mark[rt] = false;
}
lazy_x[rt] += S; lazy_y[rt] += T;
len = r - l + 1; Slen = S * len; Tlen = T * len;
sum_xx[rt] += S * 2 * sum_x[rt] + S * Slen;
sum_xy[rt] += sum_x[rt] * T + sum_y[rt] * S + S * Tlen;
sum_x[rt] += Slen;
sum_y[rt] += Tlen;
}
void pushdown(int rt, int l, int r) {
if (lazy_x[rt] || lazy_y[rt]) {
int mid = (l + r) >> 1;
update(rt << 1, l, mid, lazy_x[rt], lazy_y[rt]);
update(rt << 1 | 1, mid + 1, r, lazy_x[rt], lazy_y[rt]);
lazy_x[rt] = lazy_y[rt] = 0;
}
if (cover_mark[rt]) {
int mid = (l + r) >> 1;
coverit(rt << 1, l, mid, cover_x[rt], cover_y[rt]);
coverit(rt << 1 | 1, mid + 1,r, cover_x[rt], cover_y[rt]);
cover_mark[rt] = false;
}
}
void pushup(int rt) {
sum_x[rt] = sum_x[rt << 1] + sum_x[rt << 1 | 1];
sum_y[rt] = sum_y[rt << 1] + sum_y[rt << 1 | 1];
sum_xx[rt] = sum_xx[rt << 1] + sum_xx[rt << 1 | 1];
sum_xy[rt] = sum_xy[rt << 1] + sum_xy[rt << 1 | 1];
}
void build(int rt, int l, int r) {
if (l == r) {
xl = x[l]; yl = y[l];
sum_x[rt] = xl; sum_y[rt] = yl;
sum_xx[rt] = xl * xl;
sum_xy[rt] = xl * yl;
cover_mark[rt] = false;
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void add(int rt, int l, int r, int L, int R, const double &S, const double &T) {
if (L <= l && r <= R) {
update(rt, l, r, S, T);
return;
}
int mid = (l + r) >> 1;
pushdown(rt, l, r);
if (L <= mid) add(rt << 1, l, mid, L, R, S, T);
if (R > mid) add(rt << 1 | 1, mid + 1, r, L, R, S, T);
pushup(rt);
}
void cover(int rt, int l, int r, int L, int R, const double &S, const double &T) {
if (L <= l && r <= R) {
coverit(rt, l, r, S, T);
return;
}
int mid = (l + r) >> 1;
pushdown(rt, l, r);
if (L <= mid) cover(rt << 1, l, mid, L, R, S, T);
if (R > mid) cover(rt << 1 | 1, mid + 1, r, L, R, S, T);
pushup(rt);
}
PP getans(int rt, int l, int r, int L, int R) {
if (L <= l && r <= R) return PP(sum_x[rt], sum_y[rt], sum_xx[rt], sum_xy[rt]);
PP tl, tr;
int mid = (l + r) >> 1;
pushdown(rt, l, r);
if (L <= mid) tl = getans(rt << 1, l, mid, L, R);
if (R > mid) tr = getans(rt << 1 | 1, mid + 1, r, L, R);
return tl + tr;
}
}
int main() {
freopen("relative.in", "r", stdin);
freopen("relative.out", "w", stdout);
n = in(); m = in(); //scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lf", x + i);
for (int i = 1; i <= n; ++i) scanf("%lf", y + i);
ST::build(1, 1, n);
int L, R, op; double S, T, xb, yb, fz, fm;
PP r;
while (m--) {
op = in(); //scanf("%d", &op);
if (op == 1) {
L = in(); R = in();//scanf("%d%d", &L, &R);
r = ST::getans(1, 1, n, L, R);
xb = r.sumx / (R - L + 1);
yb = r.sumy / (R - L + 1);
fz = r.sumxy - r.sumy * xb - r.sumx * yb + xb * yb * (R - L + 1);
fm = r.sumxx - r.sumx * 2 * xb + xb * xb * (R - L + 1);
printf("%.10lf\n", fz / fm);
} else if (op == 2) {
L = in(); R = in();
scanf("%lf%lf", &S, &T);
ST::add(1, 1, n, L, R, S, T);
} else {
L = in(); R = in();
scanf("%lf%lf", &S, &T);
ST::cover(1, 1, n, L, R, S, T);
}
/*
for (int i = 1; i <= n; ++i) {
PP r = ST::getans(1, 1, n, i, i);
printf("(%.3lf, %.3lf) ", r.sumx, r.sumy);
}
puts("");*/
}
return 0;
}
SDOI 2017 Round1 解题报告的更多相关文章
- 2017 SCNUCPC 解题报告
校内赛题目.解题思路.参考代码一览 A. Blackstorm's Blackstore Problem Description Blackstorm is going to open a black ...
- NOIP 2017 Day1 解题报告
总分:100分 T1,小凯的疑惑, 100分 T2,时间复杂度,0分 T3,逛公园,0分 T1 ###题意简化: 给定两个互质的数字,输出最大不能表示的数: 基础数论题目 代码: #include&l ...
- BZOJ 3990 [SDOI 2015] 排序 解题报告
这个题哎呀...细节超级多... 首先,我猜了一个结论.如果有一种排序方案是可行的,假设这个方案是 $S$ . 那么我们把 $S$ 给任意重新排列之后,也必然可以构造出一组合法方案来. 于是我们就可以 ...
- [雅礼NOIP集训 2017] number 解题报告 (组合数+二分)
题解: 令$S(i)={i+1,i+2,...,i<<1}$,f(i,k)表示S(i)中在二进制下恰好有k个1的数的个数 那么我们有$f(i,k)=\sum_{x=1}^{min(k,p) ...
- ACM-ICPC 2017 Asia HongKong 解题报告
ACM-ICPC 2017 Asia HongKong 解题报告 任意门:https://nanti.jisuanke.com/?kw=ACM-ICPC%202017%20Asia%20HongKon ...
- 【LeetCode】Island Perimeter 解题报告
[LeetCode]Island Perimeter 解题报告 [LeetCode] https://leetcode.com/problems/island-perimeter/ Total Acc ...
- 【九度OJ】题目1026:又一版 A+B 解题报告
[九度OJ]题目1026:又一版 A+B 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1026 题目描述: 输入两个不超过 ...
- 【九度OJ】题目1124:Digital Roots 解题报告
[九度OJ]题目1124:Digital Roots 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1124 题目描述: T ...
- 【九度OJ】题目1074:对称平方数 解题报告
[九度OJ]题目1074:对称平方数 解题报告 标签(空格分隔): 九度OJ 原题地址:http://ac.jobdu.com/problem.php?pid=1074 题目描述: 打印所有不超过n( ...
随机推荐
- 【BZOJ4565】【HAOI2016】字符合并 [状压DP][区间DP]
字符合并 Time Limit: 20 Sec Memory Limit: 256 MB[Submit][Status][Discuss] Description 有一个长度为 n 的 01 串,你 ...
- 51nod1149 Pi的递推式
基准时间限制:1 秒 空间限制:131072 KB 分值: 640 F(x) = 1 (0 <= x < 4) F(x) = F(x - 1) + F(x - pi) (4 <= x ...
- 第七周 ch04 课下测试补交
2017-2018-1 20155335 <信息安全系统设计基础>第7周 课下测试博客 本人不慎忘记去交dao'zhi 测试题目: SEQ+对SEQ的改变有() A . PC的计算挪到取指 ...
- flex实例(阮一峰)
Flex 布局教程:实例篇 作者: 阮一峰 日期: 2015年7月14日 上一篇文章介绍了Flex布局的语法,今天介绍常见布局的Flex写法. 你会看到,不管是什么布局,Flex往往都可以几行命令 ...
- HDU 2516 取石子游戏 (找规律)
题目链接 Problem Description 1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍.取完者胜.先取者负输出" ...
- hdu 2475 BOX (splay)
版权声明:本文为博主原创文章,未经博主允许不得转载. hdu 2475 Splay树是一种神奇的东西... 题意: 有一些箱子,要么放在地上,要么放在某个箱子里面 . 现在有两种操作: (1) MOV ...
- 设计模式之笔记--外观模式(Facade)
外观模式(Facade) 定义 外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 类图 描述 Facade:外观类,外观 ...
- 处理tomcat内存溢出问题
TOMCAT起步内存溢出问题Exception in thread ""http-bio-8080"-exec-java.lang.OutOfMemoryError: P ...
- python模块之xml.etree.ElementTree
xml.etree.ElementTree用于解析和构建XML文件 <?xml version="1.0"?> <data> <country nam ...
- java并发-同步容器类
java平台类库包含了丰富的并发基础构建模块,如线程安全的容器类以及各种用于协调多个相互协作的线程控制流的同步工具类. 同步容器类 同步容器类包括Vector和Hashtable,是早期JDK的一部分 ...