省选测试4
省选测试 4
T1
给你一个 \(n\) 维的多面体,定义它的面是一个 \(n-1\) 维多面体,已知它必定有 \(2n\)面。
我们把这个 \(n\) 维体的所有面都染色。
现在,给你这个多面体每一维的长度,然后可以将这个\(n\)维体划分成 \(\displaystyle \sum_{i=1}^na_i\)个每个边的边长为1的小 \(n\) 维体。
定义一个小 \(n\) 维体的权值是它有多少个面被染色。
输出每个权值的小 \(n\) 维体的个数,显然可以知道,权值范围在 [0, \(2n\)] 中,所以只需要把 [0, \(2n\)] 的所有答案都输出即可。
答案对469762049取模. \(n <= 100000\)
多项式.
\(a_i = 1\)时, 我们发现原来所有的小\(i- 1\)椎体都会增加染色面两个面变成\(i\)锥体.
\(a_i >= 2\)时, 我们发现只有两遍的小\(i - 1\)椎体会增加一个染色面, 中间的那些虽然也增加了两个面但是并没有被染色, 所以染色面数不变.
所以上面两种情况分别对应着多项式 : \(x^2, 2x+(a_i - 2)\).
然后用分治NTT就好了. 但是我还不会, 于是我用了分治 + NTT, 时间复杂度还是\(O(n\log n)\)的.
#include <bits/stdc++.h>
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 3e5 + 5, mod = 469762049, g = 3;
int n;
int a[N];
int F[N << 1], rev[N << 1];
int ksm(int x, int y) {
int res = 1;
while(y) { if(y & 1) res = 1ll * res * x % mod; x = 1ll * x * x % mod; y >>= 1; }
return res;
}
void NTT(int len, int *a, int f) {
for(int i = 0;i < len; i++) if(rev[i] > i) swap(a[i], a[rev[i]]);
for(int i = 1;i < len; i <<= 1) {
int w = ksm(g, (mod - 1) / (i << 1));
if(f == -1) w = ksm(w, mod - 2);
for(int j = 0;j < len; j += (i << 1)) {
int k = 1;
for(int l = 0;l < i; l++, k = 1ll * k * w % mod) {
int x = a[j + l], y = 1ll * k * a[j + l + i] % mod;
a[j + l] = (x + y) % mod;
a[j + l + i] = (x - y + mod) % mod;
}
}
}
if(f == -1) {
int INV = ksm(len, mod - 2);
for(int i = 0;i < len; i++) a[i] = 1ll * a[i] * INV % mod;
}
}
void Divide(int l, int r, int *f, int L) {
if(l > r) return ;
if(l == r) {
if(a[l] == 1) f[2] = 1;
if(a[l] >= 2) f[0] = (a[l] - 2), f[1] = 2;
return ;
}
int mid = (l + r) >> 1;
Divide(l, mid, f, (mid - l + 1) * 2 + 1);
int g[L << 2];
memset(g, 0, sizeof(g));
Divide(mid + 1, r, g, (r - mid) * 2 + 1);
// cout << l << " " << r << "------------->\n";
// for(int i = 0;i <= (mid - l + 1) * 2 + 1; i++) cout << f[i] << " "; cout << "\n";
// for(int i = 0;i <= (r - mid) * 2 + 1; i++) cout << g[i] << " "; cout << "\n";
int len = 1, t = -1;
while(len < (L << 1)) len <<= 1, t ++;
for(int i = 0;i < len; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << t);
NTT(len, f, 1); NTT(len, g, 1);
for(int i = 0;i < len; i++) f[i] = 1ll * f[i] * g[i] % mod;
NTT(len, f, -1);
for(int i = L;i <= len; i++) f[i] = 0;
}
int main() {
freopen("poly.in","r",stdin); freopen("poly.out","w",stdout);
n = read();
for(int i = 1;i <= n; i++) a[i] = read();
Divide(1, n, F, 2 * n + 1);
for(int i = 0;i <= 2 * n; i++) printf("%d\n", F[i]);
fclose(stdin); fclose(stdout);
return 0;
}
/*
3
200 203 15
*/
T2
给定\(n\)个形如\(y = kx +b\)的方程,有\(m\)个操作.
\(opt=1 : L, R, x,\)将\(x\)带入区间\([L,R]\)的方程求最大值.
\(opt=2 : p, v\)将\(k_p\)增加为\(v\).
\(opt=3 : L, R, v\)将\(b_L...b_R += v\).
\(n <= 200000, m <= 2000000\).
分块 + 李超线段树.
之前没有学过李超线段树, 学习了一下, 发现这类维护方程的题都可以用它做, 还是很妙的.
先分块, 然后对每个块内的所有方程放到一个李超线段树中去维护. 注意有一个细节 : 李超线段树中直接存\(k, b\)比存最优线段的标号更加方便, 因为更改的时候存标号的话可能会有冲突.
对于单点修改\(k\), 我们相当于得到了一条新的直线, 直接插到李超线段树中就好了, 为什么不用删除呢? 因为原来的那条直线一定不优了.
对于区间修改\(b\), 我们分块改, 对于散块暴力改, 对于整块, 那就相当于把这一块内的所有直线都向上平移了一段, 所以只需要维护一个整块加的标记就好了.
\(O(n \sqrt n \log n)\)
#include <bits/stdc++.h>
#define ls(o) t[o].ls
#define rs(o) t[o].rs
#define mid ((l + r) >> 1)
using namespace std;
inline long long read() {
long long s = 0, f = 1; char ch;
while(!isdigit(ch = getchar())) (ch == '-') && (f = -f, 1);
for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
return s * f;
}
const int N = 2e5 + 5, M = 550, inf = 1000;
int n, m, tot, c_;
int L[N], R[N], rt[N], pos[N];
long long add_b[N];
struct Line { long long k, b; } a[N];
struct point { int ls, rs, Max; long long k, b; } t[N * 50];
inline long long F(long long k, long long b, int i, int x) {
return 1ll * x * k + b + add_b[pos[i]];
}
inline double get_inter(long long k, long long b, long long K, long long B) {
return 1.0 * (B - b) / (k - K);
}
struct Lichao_tree {
void change(int &o, int l, int r, long long K, long long B, int i) {
if(!o) {
o = ++ tot; t[o].k = K; t[o].b = B; t[o].Max = i;
return ;
}
if(l == r) {
long long x = F(t[o].k, t[o].b, t[o].Max, l), y = F(K, B, i, l);
if(y > x) t[o].k = K, t[o].b = B, t[o].Max = i;
return ;
}
long long x = F(t[o].k, t[o].b, t[o].Max, l), y = F(t[o].k, t[o].b, t[o].Max, r), x_ = F(K, B, i, l), y_ = F(K, B, i, r);
double z = get_inter(t[o].k, t[o].b, K, B);
if(x_ > x && y_ > y) { t[o].k = K; t[o].b = B; t[o].Max = i; return ; }
if(x_ <= x && y_ <= y) { return ; }
if(x_ > x && y_ <= y) {
if(z <= mid) change(ls(o), l, mid, K, B, i);
if(z > mid) { change(rs(o), mid + 1, r, t[o].k, t[o].b, t[o].Max); t[o].k = K; t[o].b = B; t[o].Max = i; }
}
if(x_ <= x && y_ > y) {
if(z <= mid) { change(ls(o), l, mid, t[o].k, t[o].b, t[o].Max); t[o].k = K; t[o].b = B; t[o].Max = i; }
if(z > mid) change(rs(o), mid + 1, r, K, B, i);
}
}
long long query(int o, int l, int r, int k) {
if(!o) return 0;
if(l == r) return F(t[o].k, t[o].b, t[o].Max, k);
long long res = F(t[o].k, t[o].b, t[o].Max, k);
if(k <= mid) return max(res, query(ls(o), l, mid, k));
if(k > mid) return max(res, query(rs(o), mid + 1, r, k));
}
} T[M];
void build() {
for(register int i = 1;i <= n; i++) T[pos[i]].change(rt[pos[i]], 1, inf, a[i].k, a[i].b, i);
}
inline long long query(int l, int r, int k) {
int x = pos[l], y = pos[r];
// cout << x << " " << y << "---------->\n";
long long res = 0;
if(x == y) {
for(register int i = l;i <= r; i++) res = max(res, F(a[i].k, a[i].b, i, k));
}
else {
// cout << T[x].query(rt[x], 1, inf, k) << "\n";
for(register int i = l;i <= R[x]; i++) res = max(res, F(a[i].k, a[i].b, i, k));
for(register int i = x + 1;i <= y - 1; i++) res = max(res, T[i].query(rt[i], 1, inf, k));
for(register int i = L[y];i <= r; i++) res = max(res, F(a[i].k, a[i].b, i, k));
}
return res;
}
inline void change_k(int p, int v) {
a[p].k += v;
int x = pos[p];
T[x].change(rt[x], 1, inf, a[p].k, a[p].b, p);
}
inline void change_b(int l, int r, int v) {
int x = pos[l], y = pos[r];
if(x == y) {
for(register int i = l;i <= r; i++) {
a[i].b += v; T[x].change(rt[x], 1, inf, a[i].k, a[i].b, i);
}
}
else {
for(register int i = l;i <= R[x]; i++) {
a[i].b += v; T[x].change(rt[x], 1, inf, a[i].k, a[i].b, i);
}
for(register int i = x + 1;i <= y - 1; i++) add_b[i] += v;
for(register int i = L[y];i <= r; i++) {
a[i].b += v; T[y].change(rt[y], 1, inf, a[i].k, a[i].b, i);
}
}
}
int main() {
// freopen("data_struct.in","r",stdin); freopen("data_struct.out","w",stdout);
n = read(); m = read(); int B = sqrt(n); c_ = n;
for(int i = 1;i <= n; i++) a[i].k = read();
for(int i = 1;i <= n; i++) a[i].b = read();
for(int i = 1;i <= n; i++) L[i] = 2333333;
for(int i = 1;i <= n; i++) pos[i] = (i - 1) / B + 1;
for(int i = 1;i <= n; i++) L[pos[i]] = min(L[pos[i]], i), R[pos[i]] = max(R[pos[i]], i);
// for(int i = 1;i <= n; i++) cout << F(i, 1) << "\n";
build();
for(int i = 1, opt, l, r, x;i <= m; i++) {
opt = read();
if(opt == 1) {
l = read(); r = read(); x = read();
printf("%lld\n", query(l, r, x));
}
if(opt == 2) {
l = read(); x = read();
change_k(l, x);
}
if(opt == 3) {
l = read(); r = read(); x = read();
change_b(l, r, x);
}
}
// fclose(stdin); fclose(stdout);
return 0;
}
T3
到现在题目还没读懂......
从上次打完最后的副本之后, 她决心想要出一个有价值的题,然后就有了这道题
现在, 她想给小朋友们发糖(大雾),她现在有 n 个薛定谔的糖果盒,她可以进行 m 次如下的三个操作
- +1s
- 选出 k 个糖果盒,每个糖果盒有 p% 的概率开出糖果。(可以包含已经打开的糖果盒,由于每个糖果盒是量子态的,所以可能之前有糖果的糖果盒这次扣上盒盖的后,可能会变成一个空盒)
- 选出来 T 个礼品盒,第 i 个糖果盒有min(P + i - 1,100)% 的概率开出糖果
现在ta想要知道,她期望最多能拿出多少个糖果。
结果保留 5 位小数即可
\(P <= 100, T <= 20, N <= 500, M <= 500,Test <= 10\)