余弦
Description
给定长度为 𝑁 的实数序列 𝐴𝑖(1 ≤ 𝑖 ≤ 𝑁), 你需要在数列上进行两类操作:
1. 把 𝑙 ≤ 𝑖 ≤ 𝑟 中的每个 𝐴𝑖 加上实数 𝑣。
2. 求 𝑙 ≤ 𝑖 ≤ 𝑟 中 cos(𝐴𝑖) 的和。
Input
输入包含多组数据,最开头的正整数 T (T≤ 5) 指明了数据组数。
对于每组数据:第一行 2 个整数 N、M,表示数列长度与操作个数。
第二行 N 个实数 𝐴𝑖(|𝐴𝑖| ≤ 100),小数点后最多有 3 位小数。
接下来 M 行每行形如“1 𝑙 𝑟 𝑣” 或 “2 𝑙 𝑟”。
前者表示一个第 1 类操作,后者表示一个第 2 类操作。
其中 𝑙, 𝑟(1 ≤ 𝑙 ≤ 𝑟 ≤ 𝑁) 是整数,而𝑣(|𝑣| ≤ 100)是实数,小数点后最多有 3位小数。
Output
对于每组数据,先输出一行 "Case #k:",其中 k 是该组数据的编号,从 1 开始。
对于每组数据中的每个第 2 类操作,输出一个实数表示答案,保留 3 位小数。
Sample Input 1
3 3 3 0 -2 -6 1 1 2 -7 2 1 2 2 2 3 5 4 4 2 4 -0 -4 1 1 4 2 2 4 5 1 3 5 5 2 2 3 7 3 0.123 -19.002 -57.507 34.434 -80.886 -17.115 69.843 2 2 7 1 2 7 16.341 2 1 3
Sample Output 1
Case #1: -0.157 0.049 Case #2: -1.070 -0.649 Case #3: 1.854 -0.842
Hint
对于 20%的数据,1 ≤ N, M ≤ 1000。
对于另外 20%的数据,没有 1 类操作。
对于另外 20%的数据,1 类操作均在 2 类操作之前。
对于 100%的数据,1 ≤ N, M ≤ 200000。
(我不会说我因为传参写错了而调了一晚上的);
其实是水题;
你在每个节点维护sin值的和还有cos值的和;
你加一个数, 根据高一的知识,∑cos(ai+c)=∑(cosai*cosc-sinai*sinc);
而上面的式子可以化简为∑cos(ai+c)= cosc∑cosai - sincΣsinai , 显然可以快速合并;
合并sin值同理;
代码奉上:
//By zZhBr #include <iostream> #include <cstdio> #include <cmath> #include <cstring> using namespace std; inline int read() { int res=0;bool flag=0;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')flag=1;ch=getchar(); }while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch-'0');ch=getchar(); }return flag?-res:res; } const int N = 200010; const double eps = 1e-9; int n, m, T; double a[N]; int cnt = 1; struct SegmentTree { int l, r; double sinn, coss; double flag; void clean() { l = r = 0; sinn = coss = 0; flag = 0; } }t[N<<2]; #define ls(x) x << 1 #define rs(x) x << 1 | 1 inline void pushup(int o) { t[o].sinn = t[ls(o)].sinn + t[rs(o)].sinn; t[o].coss = t[ls(o)].coss + t[rs(o)].coss; t[o].l = t[ls(o)].l, t[o].r = t[rs(o)].r; } inline void build(int l, int r, int o) { if (l == r) { t[o].l = t[o].r = l; t[o].sinn = sin(a[l]); t[o].coss = cos(a[l]); t[o].flag = 0.0; return; } int mid = l + r >> 1; build(l, mid, o<<1); build(mid + 1, r, o<<1|1); pushup(o); } inline void spread(int o) { if (t[o].flag == 0) return; double c = t[o].flag; double X = t[ls(o)].coss, Y = t[ls(o)].sinn; t[ls(o)].coss = cos(c) * X - sin(c) * Y; t[ls(o)].sinn = sin(c) * X + cos(c) * Y; X = t[rs(o)].coss, Y = t[rs(o)].sinn; t[rs(o)].coss = cos(c) * X - sin(c) * Y; t[rs(o)].sinn = sin(c) * X + cos(c) * Y; t[ls(o)].flag += c; t[rs(o)].flag += c; t[o].flag = 0; } inline void change(int li, int ri, int o, double v) { if (li <= t[o].l and ri >= t[o].r) { double la=t[o].coss; t[o].coss = cos(v) * t[o].coss - sin(v) * t[o].sinn; t[o].sinn = sin(v) * la + cos(v) * t[o].sinn; t[o].flag += v; return; } spread(o); int mid = t[o].l + t[o].r >> 1; if (li <= mid) change(li, ri,ls(o), v); if (ri > mid) change(li, ri, rs(o), v); pushup(o); } inline double query(int o, int li, int ri) { if (li <= t[o].l and ri >= t[o].r) { return t[o].coss; } spread(o); double res = 0; int mid = t[o].l + t[o].r >> 1; if (li <= mid) res += query(ls(o), li, ri); if (ri > mid) res += query(rs(o), li, ri); return res; } inline void init(int x) { cnt = 1; for (register int i = 1 ; i <= x ; i ++) { t[i].clean(); } } int main() { T = read(); for (register int tiime = 1 ; tiime <= T ; tiime++) { printf("Case #%d:\n", tiime); n = read(), m = read(); init(n<<2); for (register int i = 1 ; i <= n ; i ++) cin >> a[i]; build(1, n, 1); while (m--) { int opt = read(); if (opt == 1) { int x = read(), y = read(); double v; cin >> v; change(x, y, 1, v); } if (opt == 2) { int x = read(), y = read(); printf("%.3lf\n", query(1, x, y)); } } init(n<<1); } }