SDOI 2017 Round1 题解

来源:互联网 发布:林弯弯淘宝店铺名 编辑:程序博客网 时间:2024/06/07 12:02

Day1

Problem 1:数字表格

题目描述

Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么

f[0]=0
f[1]=1
f[n]=f[n1]+f[n2],n2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,j的最大公约数。
Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。
答案对109+7取模。

输入格式

有多组测试数据。
第一个一个数T,表示数据组数。
接下来T行,每行两个数n,m

输出格式

输出T行,第i行的数是第i组数据的结果

样例1

input

3
2 3
4 5
6 7

output

1
6
960

限制与约定

10%的数据,1n,m100
30%的数据,1n,m1000
另外存在30%的数据,T3
100%的数据,T1000,1n,m106

时间限制:5s
内存限制:128M

题解

ans=i=1nj=1mfib[gcd(i,j)]
ans=g=1min(n,m)fib[g]ni=1mj=1[gcd(i,j)=g]
进行正常的莫比乌斯反演,得到:
ans=g=1min(n,m)fib[g]min(n,m)gd=1μ(d)×ndg×mdg
D=dg,则
ans=D=1min(n,m)g|Dfib[g]μ(Dg)×nD×mD
F(D)=g|Dfib[g]μ(Dg)

F(D)可以nlogn预处理。
ans=D=1min(n,m)F(D)nD×mD

nD×mD取值只有n+m种,进行分块。
所以总的复杂度为:O(nlogn+T×(n+m)logmod)

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long ll;const int maxn = 1000010, p = 1e9+7;ll fib[maxn], Nfib[maxn], F[maxn], NF[maxn];int prime[maxn], mu[maxn], pcnt, T, n, m, MX;bool is[maxn];ll pow(ll x, ll y){    ll res = 1;    for( ; y; y >>= 1, x = (x*x) % p) if(y & 1) res = (res * x) % p;    return res;}void pre(){    is[1] = mu[1] = fib[1] = NF[0] = 1;    for(int i = 2; i <= MX; i ++){        if(!is[i]) mu[i] = -1, prime[++ pcnt] = i;        for(int j = 1; i * prime[j] <= MX; j ++){            is[i * prime[j]] = 1;            if(i % prime[j] == 0) break;            else mu[i*prime[j]] = - mu[i];        }    }    for(int i = 2; i <= MX; i ++) fib[i] = (fib[i-1] + fib[i-2]) % p;    for(int i = 1; i <= MX; i ++) Nfib[i] = pow(fib[i], p-2);    for(int i = 1; i <= MX; i ++) F[i] = 1;    for(int i = 1; i <= MX; i ++)        for(int j = 1; i*j <= MX; j ++)            if(mu[j]) F[i*j] = F[i*j] * (mu[j] == 1 ? fib[i] : Nfib[i]) % p;    for(int i = 2; i <= MX; i ++) F[i] = (F[i] * F[i-1]) % p;    for(int i = 1; i <= MX; i ++) NF[i] = pow(F[i], p-2);    return;}int main(){    MX = 1000000; pre();    scanf("%d", &T);    while(T --){        scanf("%d%d", &n, &m);        if(n > m) swap(n, m);        int last;        ll ans = 1;        for(int i = 1; i <= n; i = last+1){            last = min(n/(n/i), m/(m/i));            ans = (ans * pow(((F[last] * NF[i-1]) % p + p) % p, 1LL * (n/i) * (m/i) % (p-1))) % p;        }        printf("%lld\n", ans);    }    return 0;}

Problem 2:树点涂色

题目描述

Bob有一棵n个点的有根树,其中1号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。
定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。
Bob可能会进行这几种操作:

  • 1 x

把点x到根节点的路径上所有的点染上一种没有用过的新颜色。

  • 2 x y

xy的路径的权值。

  • 3 x

在以x为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。
Bob一共会进行m次操作

输入格式

第一行两个数n,m
接下来n1行,每行两个数a,b,表示ab之间有一条边。
接下来m行,表示操作,格式见题目描述

输出格式

每当出现2,3操作,输出一行。
如果是2操作,输出一个数表示路径的权值
如果是3操作,输出一个数表示权值的最大值

样例1

input

5 6
1 2
2 3
3 4
3 5
2 4 5
3 3
1 4
2 4 5
1 5
2 4 5

output

3
4
2
2

限制与约定

共10个测试点
测试点1,1n,m1000
测试点2、3,没有2操作
测试点4、5,没有3操作
测试点6,树的生成方式是,对于i(2in),在1到i1中随机选一个点作为i的父节点。
测试点7,1n,m50000
测试点8,1n50000
测试点9,10,无特殊限制
对所有数据,1n1051m105

时间限制:1s
空间限制:128MB

题解

先对树进行树链剖分,线段树每个节点维护该点到跟的路径上的权值。
LCTAccess维护覆盖新颜色对权值的影响。
一开始整棵树由轻链连接,每一次修改颜色的的操作是把这个点到跟的路径上的所有点变成一种没有出现过的颜色,对应到LCT中,就相当于对一个点进行Access的操作,而一个点的答案就是这个点到跟经过的轻链个数+1,一条虚边改为实边的操作相当于线段树上对子树答案-1,一条实边改为虚边相当于把线段树上子树的答案+1.

第一种操作,我们Access到点x的路径,每一个路径如果发生了实边与虚边的变化就更新答案。

第二种操作,所有的操作都可以总结为下面这张图:

  • A区域内的颜色在左右计算的时候都会被计算一次,所以计算了两次。
  • B区域内的颜色在左右计算的时候都会被计算一次,所以计算了两次。
  • C区域内的颜色只被左边计算了一次。
  • D区域内的颜色只被右边计算了一次。

因为B区域被减了两次,但实际需要被算入答案一次,所以答案是两个询问点的权值和减去两倍的LCA的权值+1

第三种操作,就是线段树的区间最大值。

时间复杂度:O((n+mlogn)×logn)

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 100010;struct node{    int id;    node *pre, *ch[2];    bool is_root(){return pre->ch[0] != this && pre->ch[1] != this;}    void set_ch(int wh, node *child);    int wh(){return pre->ch[0] == this ? 0 : 1;}}po[maxn], *null;void node::set_ch(int wh, node *child){    ch[wh] = child;    if(child != null) child->pre = this;}int n, m, ct;int head[maxn], to[maxn<<1], nxt[maxn<<1], cnt;int dep[maxn], sz[maxn], fa[maxn], pos[maxn], q[maxn], st[maxn];int son[maxn], dfn[maxn], tp[maxn];bool vis[maxn];int tree[maxn*3], lazy[maxn*3];void add(int a, int b){    nxt[++ cnt] = head[a], to[head[a] = cnt] = b;}#define mid ((l+r)>>1)#define lch ((now<<1))#define rch ((now<<1)|1)void build(int now, int l, int r){    if(l == r){        tree[now] = dep[dfn[l]];        return;    }    build(lch, l, mid);    build(rch, mid+1, r);    tree[now] = max(tree[lch], tree[rch]);}void pre(){    dep[1] = 1;    int l = 1, r = 0;    q[++ r] = 1, vis[1] = 1;     while(l <= r){        int x = q[l ++]; sz[x] = 1;        for(int i = head[x]; i; i = nxt[i]){            int u = to[i]; if(vis[u]) continue;            fa[u] = x, dep[u] = dep[x] + 1;            q[++ r] = u, vis[u] = 1;        }    }    for(int i = n; i >= 2; i --) sz[fa[q[i]]] += sz[q[i]];    for(int x = 1; x <= n; x ++){        for(int i = head[x]; i; i = nxt[i]){            int u = to[i]; if(u == fa[x]) continue;            if(sz[son[x]] < sz[u]) son[x] = u;        }    }    int top = 0;    st[++ top] = 1, tp[1] = 1;    while(top){        int x = st[top --];        pos[x] = ++ ct, dfn[ct] = x;        for(int i = head[x]; i; i = nxt[i]){            int u = to[i]; if(u == son[x] || u == fa[x]) continue;            st[++ top] = u, tp[u] = u;        }        if(son[x]) st[++ top] = son[x], tp[son[x]] = tp[x];    }    for(int i = 1; i <= n; i ++){        po[i].ch[0] = po[i].ch[1] = null;        po[i].id = i, po[i].pre = &po[fa[i]];    }    build(1, 1, n);}void down(int now, int l, int r){    if(!lazy[now]) return;    int &z = lazy[now];    tree[lch] += z, tree[rch] += z;    lazy[lch] += z, lazy[rch] += z;    z = 0;}void modify(int now, int l, int r, int pos1, int pos2, int c){    if(l == pos1 && r == pos2){        lazy[now] += c;        tree[now] += c;        return;    }down(now, l, r);    if(pos2 <= mid) modify(lch, l, mid, pos1, pos2, c);    else if(pos1 >= mid+1) modify(rch, mid+1, r, pos1, pos2, c);    else modify(lch, l, mid, pos1, mid, c), modify(rch, mid+1, r, mid+1, pos2, c);    tree[now] = max(tree[lch], tree[rch]);}int que(int now, int l, int r, int pos1, int pos2){    if(l == pos1 && r == pos2) return tree[now];    down(now, l, r);    if(pos2 <= mid) return que(lch, l, mid, pos1, pos2);    else if(pos1 >= mid+1) return que(rch, mid+1, r, pos1, pos2);    else return max(que(lch, l, mid, pos1, mid), que(rch, mid+1, r, mid+1, pos2));}int Lca(int x, int y){    if(x == y) return x;    while(tp[x] != tp[y]){        if(dep[tp[x]] < dep[tp[y]]) swap(x, y);        x = fa[tp[x]];    }    return dep[x] < dep[y] ? x : y;}void rotate(node *now){    node *fa = now->pre, *gra = fa->pre;    int wh = now->wh();    if(!fa->is_root()) gra->ch[gra->ch[0] == fa ? 0 : 1] = now;    fa->set_ch(wh, now->ch[wh^1]);    now->set_ch(wh^1, fa), now->pre = gra;}void splay(node *now){    for( ; !now->is_root(); rotate(now))        if(!now->pre->is_root())            now->wh() == now->pre->wh() ? rotate(now->pre) : rotate(now);}void access(node *x){    for(node *i = null; x != null; i = x, x = x->pre){        splay(x);        if(x->ch[1] != i){            if(x->ch[1] != null){                node *le = x->ch[1];                while(le->ch[0] != null) le = le->ch[0];                int ID = le->id;                modify(1, 1, n, pos[ID], pos[ID] + sz[ID] - 1, 1);            }            x->set_ch(1, i);            node *le = i;            while(le->ch[0] != null) le = le->ch[0];            if(i != null) modify(1, 1, n, pos[le->id], pos[le->id]+sz[le->id] - 1, -1);        }    }}int main(){    null = po;    null->pre = null->ch[0] = null->ch[1] = null;    scanf("%d%d", &n, &m);    for(int i = 1, x, y; i < n; i ++){        scanf("%d%d", &x, &y);        add(x, y), add(y, x);    }    pre();    for(int i = 1; i <= m; i ++){        int op, x, y; scanf("%d", &op);        if(op == 1){            scanf("%d", &x);            access(&po[x]);        }else if(op == 2){            scanf("%d%d", &x, &y);            int lca = Lca(x, y);            printf("%d\n", que(1, 1, n, pos[x], pos[x]) + que(1, 1, n, pos[y], pos[y]) - 2 * que(1, 1, n, pos[lca], pos[lca]) + 1);        }else{            scanf("%d", &x);            printf("%d\n", que(1, 1, n, pos[x], pos[x] + sz[x] - 1));        }    }    return 0;}

Problem 3:序列计数

题目描述

Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数。
Alice还希望,这n个数中,至少有一个数是质数。
Alice想知道,有多少个序列满足她的要求。

输入格式

一行三个数,n,m,p

输出格式

一行一个数,满足Alice的要求的序列数量,答案对20170408取模。

样例1

input

3 5 3

input

33

约定与限制

20%的数据,1n,m100
50%的数据,1m100
80%的数据,1m106
100%的数据,1n109,1m2×107,1p100

时间限制:3s
空间限制:128MB

题解

首先问题可以转换为求所有数的答案减去非质数的答案。
dp[i][j]表示使用了前i个数,%p=j的方案数,那么dp[i][j]+=dp[i1][(jk)%p]
这样就可以用矩阵快速幂了,转移矩阵的第i行,第j列的一个数表示在上个矩阵的第1行j列的数可以转移给这一个矩阵的第1行第i列多少次。
这个次数就是说有数中%p=(ji)%p的数的个数。
然后可以线性筛法预处理这个个数,在O(p2)的复杂度下完成。
时间复杂度:O(p2+p3logn)

代码

#include <cstdio>#include <algorithm>#include <cstring>#include <iostream>using namespace std;const int maxn = 20000010;const int mod = 20170408;int prime[maxn], pcnt;bool is[maxn];int nump[110], num[110], L, MX;int n, m, p;void pre(){    is[1] = 1;    num[1] = nump[1] = 1;    for(int i = 2; i <= MX; i ++){        if(!is[i]) prime[++ pcnt] = i, nump[i%p] ++;        for(int j = 1; i * prime[j] <= MX; j ++){            int w = i * prime[j];            is[w] = 1, num[w%p] ++, nump[w%p] ++;            if(i % prime[j] == 0) break;        }    }}struct Matrix{    long long a[101][101];    Matrix(){memset(a, 0, sizeof(a));}    void init(){for(int i = 0; i < L; i ++) a[i][i] = 1;}    Matrix operator * (const Matrix &t) const{        Matrix res;        for(int i = 0; i < L; i ++)            for(int j = 0; j < L; j ++)                for(int k = 0; k < L; k ++)                    res.a[i][j] = (res.a[i][j] + a[i][k] * t.a[k][j] % mod) % mod;        return res;    }}A, B, T1, T2;Matrix pow(Matrix x, int y){    Matrix res; res.init();    for( ; y; y >>= 1, x = x*x) if(y & 1) res = res * x;    return res;}int main(){    scanf("%d%d%d", &n, &m, &p), L = p, MX = m;    pre(); A.a[0][0] = B.a[0][0] = 1;    for(int i = 0; i < L; i ++){        for(int j = 0; j < L; j ++){            T1.a[i][j] = (T1.a[i][j] + nump[((j-i)%p+p)%p]) % mod;            T2.a[i][j] = (T2.a[i][j] + num[((j-i)%p+p)%p]) % mod;        }    }    A = A * pow(T1, n);    B = B * pow(T2, n);    printf("%d", ((A.a[0][0] - B.a[0][0]) % mod + mod) % mod);    return 0;}

Day2

Problem 1:新生舞会

题目描述

学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴。
n个男生和n个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴。
Cathy收集了这些同学之间的关系,比如两个人之前认识没计算得出 ai,j ,表示第i个男生和第j个女生一起跳舞时他们的喜悦程度。
Cathy还需要考虑两个人一起跳舞是否方便,比如身高体重差别会不会太大,计算得出 bi,j,表示第i个男生和第j个女生一起跳舞时的不协调程度。
当然,还需要考虑很多其他问题。
Cathy想先用一个程序通过ai,jbi,j求出一种方案,再手动对方案进行微调。
Cathy找到你,希望你帮她写那个程序。
一个方案中有n对舞伴,假设没对舞伴的喜悦程度分别是a1,a2,...,an,假设每对舞伴的不协调程度分别是b1,b2,...,bn。令

C=a1+a2+...+anb1+b2+...+bn

Cathy希望C值最大。

输入格式

第一行一个整数n
接下来n行,每行n个整数,第i行第j个数表示ai,j
接下来n行,每行n个整数,第i行第j个数表示bi,j

输出格式

一行一个数,表示C的最大值。四舍五入保留6位小数,选手输出的小数需要与标准输出相等。

样例1

input

3
19 17 16
25 24 23
35 36 31
9 5 6
3 4 2
7 8 9

output

5.357143

限制与约定

对于 10% 的数据,1n5
对于 40% 的数据,1n18
另外存在 20% 的数据,bi,j=1​​;
对于 100% 的数据,1n100,1ai,j104,1bi,j104

时间限制:1s
内存限制:128M

题解

把所有的分母乘到右边,在减回来,这样可以二分C的取值,判断当前式子的最大值是否大于0,问题转换为验证一个C的值是否合法,这就是经典的二分图最大权匹配,用SPFA费用流解决。
还要加上有理有据的常数优化才可以通过。

代码

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;typedef long long ll;const int maxn = 410;const int maxm = 40010;const int inf = 2e9+1;int n;int a[maxn][maxn], b[maxn][maxn], CC;int head[maxn], nxt[maxm], to[maxm], flow[maxm], mxflow[maxm];int q[maxm], l, r, epre[maxn], ppre[maxn], S, T, MX, cnt;double dis[maxn], cost[maxm];bool vis[maxn];void add(int a, int b, int c, double d){    nxt[++ cnt] = head[a], to[head[a] = cnt] = b, flow[cnt] = 0, mxflow[cnt] = c, cost[cnt] = d;    nxt[++ cnt] = head[b], to[head[b] = cnt] = a, flow[cnt] = 0, mxflow[cnt] = 0, cost[cnt] = -d;}bool spfa(){    for(int i = 0; i <= MX; i ++){        dis[i] = inf;        vis[i] = 0;        epre[i] = ppre[i] = 0;    }    l = 1, r = 0, q[++ r] = S, vis[S] = 1, dis[S] = 0;    while(l <= r){        int x = q[l ++]; vis[x] = 0;        for(int i = head[x]; i; i = nxt[i]){            int u = to[i];            if(mxflow[i] > flow[i] && dis[u] > dis[x] + cost[i]){                dis[u] = dis[x] + cost[i];                epre[u] = i, ppre[u] = x;                if(vis[u] == 0) q[++ r] = u, vis[u] = 1;            }        }    }    return dis[T] < inf - 2;}bool work(){    double Cost = 0;    while(spfa()){        Cost += dis[T];        if(Cost > 0) return 0;        for(int i = T; i != S; i = ppre[i]){            int e = epre[i];            flow[e] ++, flow[e^1] --;        }    }    CC = Cost;    if(Cost > 0) return 0;    return 1;}int main(){    scanf("%d", &n);    S = 2*n+1, T = S+1, MX = T+1;    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= n; j ++)            scanf("%d", &a[i][j]);    bool ok = 1;    for(int i = 1; i <= n; i ++)        for(int j = 1; j <= n; j ++){            scanf("%d", &b[i][j]);            if(b[i][j] != 1) ok = 0;        }    if(ok){        cnt = 1;        for(int i = 1; i <= n; i ++){            for(int j = 1; j <= n; j ++){                add(i, n+j, 1, -a[i][j]);            }        }        for(int i = 1; i <= n; i ++) add(S, i, 1, 0), add(i+n, T, 1, 0);        work();        printf("%.6lf", -1.0 * CC / n);        return 0;    }    double l = 0, r = 10000;    while(r - l >= 0.00000001){        double mid = (r+l)/2;        cnt = 1;        for(int i = 1; i <= MX; i ++) head[i] = 0;        for(int i = 1; i <= n; i ++){            for(int j = 1; j <= n; j ++){                add(i, n+j, 1, - a[i][j] + mid * b[i][j]);            }        }        for(int i = 1; i <= n; i ++) add(S, i, 1, 0), add(n+i, T, 1, 0);        if(work()) l = mid;        else r = mid;    }printf("%.6lf", (l+r)/2);    return 0;}

Problem 2:硬币游戏

题目描述

周末同学们非常无聊,有人提议,咱们扔硬币玩吧,谁扔的硬币正面次数多谁胜利。
大家纷纷觉得这个游戏非常符合同学们的特色,但只是扔硬币实在是太单调了。
同学们觉得要加强趣味性,所以要找一个同学扔很多很多次硬币,其他同学记录下正反面情况。
用H表示正面朝上,用T表示反面朝上,扔很多次硬币后,会得到一个硬币序列。比如HTT表示第一次正面朝上,后两次反面朝上。
但扔到什么时候停止呢?大家提议,选出n个同学,每个同学猜一个长度为m的序列,当某一个同学猜的序列在硬币序列中出现时,就不再扔硬币了,并且这个同学胜利,为了保证只有一个同学胜利,同学们猜的n个序列两两不同。
很快,n个同学猜好序列,然后进入了紧张而又刺激的扔硬币环节。你想知道,如果硬币正反面朝上的概率相同,每个同学胜利的概率是多少。

输入格式

第一行两个整数n,m
接下里n行,每行一个长度为m的字符串,表示第i个同学猜的序列。

输出格式

输出n行,第i行表示第i个同学胜利的概率。
选手输出与标准输出的绝对误差不超过106即视为正确。

样例1

input

3 3
THT
TTH
HTT

output

0.3333333333
0.2500000000
0.4166666667

限制与约定

对于 10% 的数据,1n,m3
对于 40% 的数据,1n,m18
对于另外 20% 的数据,n=2
对于 100% 的数据,1n,m300

时间限制:1s
空间限制:128MB

题解



代码

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int maxn = 310;int n, m;char cc[maxn][maxn];long double ec[maxn], a[maxn][maxn];int f[maxn];int main(){    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i ++) scanf("%s", cc[i] + 1);    ec[0] = 1;    for(int i = 1; i <= m; i ++) ec[i] = 0.5 * ec[i-1];    for(int i = 1; i <= n; i ++){        int p = 0;        for(int j = 2; j <= m; j ++){            while(p && cc[i][j] != cc[i][p+1]) p = f[p];            if(cc[i][j] == cc[i][p+1]) p ++;            f[j] = p;        }        for(int j = 1; j <= n; j ++){            p = 0;            for(int k = 1; k <= m; k ++){                while(p && cc[j][k] != cc[i][p+1]) p = f[p];                if(cc[j][k] == cc[i][p+1]) p ++;            }            for( ; p; p = f[p]) a[i][j] += ec[m - p];        }        a[i][n+1] = ec[0];    }    for(int i = 1; i <= n; i ++){        for(int j = i+1; j <= n+1; j ++)            a[i][j] /= a[i][i];        a[i][i] = 1;        for(int j = i+1; j <= n; j ++){            for(int k = i+1; k <= n+1; k ++)                a[j][k] -= a[i][k] * a[j][i];            a[j][i] = 0;        }    }    for(int i = n; i >= 1; i --)        for(int j = i+1; j <= n; j ++)            a[i][n+1] -= a[i][j] * a[j][n+1];    long double sum = 0;    for(int i = 1; i <= n; i ++) sum += a[i][n+1];    for(int i = 1; i <= n; i ++)         printf("%.10lf\n", (double)((long double)a[i][n+1] / sum));    return  0;}

Problem 3:相关分析

题目描述

Frank对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度、颜色等等,进而估算出星星的距离,半径等等。
Frank不仅喜欢观测,还喜欢分析观测到的数据。他经常分析两个参数之间(比如亮度和半径)是否存在某种关系。
现在Frank要分析参数XY之间的关系。他有n组观测数据,第i组观测数据记录了xiyi。他需要一下几种操作:

  • 1 L R:

用直线拟合第L组到底R组观测数据。用x¯表示这些观测数据中x的平均数,用y¯表示这些观测数据中y的平均数,即

x¯=1RL+1i=LRxi
y¯=1RL+1i=LRyi

如果直线方程是y=ax+b,那么a应当这样计算:
ab=i=LR(xix¯)(yiy¯)i=LR(xix¯)2=y¯ax¯

你需要帮助Frank计算a

  • 2 L R S T:

Frank发现测量数据第L组到第R组数据有误差,对每个i满足LiRxi需要加上Syi需要加上T

  • 3 L R S T:

Frank发现第L组到第R组数据需要修改,对于每个i满足LiRxi需要修改为(S+i)yi需要修改为(T+i)

输入格式

第一行两个数 nm,表示观测数据组数和操作次数。
接下来一行 n 个数,第 i 个数是 xi
接下来一行 n 个数,第 i 个数是 yi
接下来 m 行,表示操作,格式见题目描述。

输出格式

对于每个 1 操作,输出一行,表示直线斜率 a
选手输出与标准输出的绝对误差或相对误差不超过 105​​即为正确。

样例1

input

3 5
1 2 3
1 2 3
1 1 3
2 2 3 -3 2
1 1 2
3 1 2 2 1
1 1 3

input

1.0000000000
-1.5000000000
-0.6153846154

约定与限制

对于 20% 的数据,1n,m1000
对于另外 20% 的数据没有 3 操作,且 2 操作中 S=0
对于另外 30% 的数据没有 3 操作;
对于 100% 的数据,1n,m105
对于所有数据,1LRn,0|S|,|T|105,0|xi|,|yi|105
对于所有数据,1 操作中不会出现分母为 0 这类特殊情况。

时间限制:1s
空间限制:128MB

题解


将上面计算a的公式展开,不难发现只需要维护x,y的和,x的平方和,x×y的和,平方和的维护可以考虑当x+1时值的变化,然后用上面维护的信息可以维护。

对于一个3操作,可以考虑先进行区间赋值为标号本身,再做2操作。
等差数列的和和平方和都可以O(1)得到答案。
时间复杂度O((n+m)logn)

代码

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int maxn = 100010;int n, m;double x[maxn], y[maxn];long double S[maxn];#define mid ((l+r)>>1)#define lch ((now<<1))#define rch ((now<<1)|1)struct node{    long double xx, yy, x2, xy;    long double tgx, tgy;    int is;    node(long double a = 0, long double b = 0, long double c = 0, long double d = 0){        xx = a, yy = b, x2 = c, xy = d;        tgx = 0, tgy = 0; is = 0;    }    node operator + (const node &t) const{        node res;        res.xx = xx + t.xx;        res.yy = yy + t.yy;        res.x2 = x2 + t.x2;        res.xy = xy + t.xy;        return res;    }}T[maxn*3];void update(int now, int l, int r){    T[now].xx = (T[lch].xx + T[rch].xx);    T[now].yy = (T[lch].yy + T[rch].yy);    T[now].x2 = (T[lch].x2 + T[rch].x2);    T[now].xy = (T[lch].xy + T[rch].xy);}void build(int now, int l, int r){    if(l == r){        T[now] = node(x[l], y[l], x[l]*x[l], x[l]*y[l]);        return;    }    build(lch, l, mid);    build(rch, mid+1, r);    update(now, l, r);}void down(int now, int l, int r){    if(T[now].is){        T[lch].is = T[rch].is = 1;        T[now].is = 0;        T[lch].tgx = T[lch].tgy = T[rch].tgx = T[rch].tgy = 0;        T[lch].yy = T[lch].xx = 1.0 * (l+mid) * (mid-l+1) / 2;        T[lch].xy = T[lch].x2 = S[mid] - S[l-1];        T[rch].yy = T[rch].xx = 1.0 * (mid+1+r) * (r-mid) / 2;        T[rch].xy = T[rch].x2 = S[r] - S[mid];    }    long double tgx = T[now].tgx, tgy = T[now].tgy;    T[lch].x2 += (mid-l+1) * tgx * tgx + 2 * tgx * T[lch].xx;    T[rch].x2 += (r-mid)   * tgx * tgx + 2 * tgx * T[rch].xx;    T[lch].xy += (mid-l+1) * tgx * tgy + T[lch].xx * tgy + T[lch].yy * tgx;    T[rch].xy += (r-mid)   * tgx * tgy + T[rch].xx * tgy + T[rch].yy * tgx;    T[lch].xx += (mid-l+1) * tgx, T[rch].xx += (r-mid) * tgx;    T[lch].yy += (mid-l+1) * tgy, T[rch].yy += (r-mid) * tgy;    T[lch].tgx += tgx, T[lch].tgy += tgy;    T[rch].tgx += tgx, T[rch].tgy += tgy;    T[now].tgx = 0, T[now].tgy = 0;}long double cx, cy;void modify(int now, int l, int r, int pos1, int pos2){    if(pos1 == l && pos2 == r){        T[now].tgx += cx, T[now].tgy += cy;        T[now].x2 += (r-l+1) * cx * cx + 2 * cx * T[now].xx;        T[now].xy += T[now].xx * cy + T[now].yy * cx + (r-l+1) * cx * cy;        T[now].xx += (r-l+1) * cx;        T[now].yy += (r-l+1) * cy;        return;    }down(now, l, r);    if(pos2 <= mid) modify(lch, l, mid, pos1, pos2);    else if(pos1 >= mid+1) modify(rch, mid+1, r, pos1, pos2);    else{        modify(lch, l, mid, pos1, mid);        modify(rch, mid+1, r, mid+1, pos2);    }    update(now, l, r);}node que(int now, int l, int r, int pos1, int pos2){    if(pos1 == l && pos2 == r) return T[now]; down(now, l, r);    if(pos2 <= mid) return que(lch, l, mid, pos1, pos2);    else if(pos1 >= mid+1) return que(rch, mid+1, r, pos1, pos2);    else return que(lch, l, mid, pos1, mid) + que(rch, mid+1, r, mid+1, pos2);}void makeis(int now, int l, int r, int pos1, int pos2){    if(pos1 == l && pos2 == r){        T[now].is = 1;        T[now].tgx = T[now].tgy = 0;        T[now].yy = T[now].xx = 1.0 * (l+r) * (r-l+1) / 2;        T[now].xy = T[now].x2 = S[r] - S[l-1];        return;    }down(now, l, r);    if(pos2 <= mid) makeis(lch, l, mid, pos1, pos2);    else if(pos1 >= mid+1) makeis(rch, mid+1, r, pos1, pos2);    else{        makeis(lch, l, mid, pos1, mid);        makeis(rch, mid+1, r, mid+1, pos2);    }    update(now, l, r);}int main(){    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i ++) S[i] = 1.0 * i * i + S[i-1];    for(int i = 1; i <= n; i ++) scanf("%lf", &x[i]);    for(int i = 1; i <= n; i ++) scanf("%lf", &y[i]);    build(1, 1, n);    for(int i = 1; i <= m; i ++){        int op, l, r, s, t;         scanf("%d", &op);        if(op == 1){            scanf("%d%d", &l, &r);            node ans;             ans = que(1, 1, n, l, r);            long double X = 1.0 * ans.xx / (r-l+1);            long double Y = 1.0 * ans.yy / (r-l+1);            long double res = ans.xy - ans.yy * X - ans.xx * Y + (r-l+1) * X * Y;            res /= (ans.x2 - 2 * ans.xx * X + (r-l+1) * X * X);            printf("%.10lf\n", (double)res);        }else if(op == 2){            scanf("%d%d%d%d", &l, &r, &s, &t);            cx = s, cy = t;            modify(1, 1, n, l, r);        }else if(op == 3){            scanf("%d%d%d%d", &l, &r, &s, &t);            makeis(1, 1, n, l, r);            cx = s, cy = t;            modify(1, 1, n, l, r);        }    }    return 0;}
2 0
原创粉丝点击