整理摘抄
学姐飞起来!
A.序列操作
(Catherine之前居然根本不知道线段树居然也能进行区间乘法……第一反应竟然是做不了)
线段树-区间乘法+加法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
int n, m;
ll a[maxn], p;
char op[5];
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct node
{
struct tree
{
ll sum, lazy, laz;
}t[maxn<<2];
void pushup(int x)
{
t[x].sum = (t[x<<1].sum + t[x<<1|1].sum) % p;
}
/*
void pushdown(int x, int l, int r)
{
int ls = x<<1, rs = x<<1|1, mid = (l + r) >> 1;
ll lz = t[x].lazy; t[x].lazy = 0;
t[ls].lazy += lz; t[ls].lazy %= p;
t[ls].sum += lz * (mid - l + 1); t[ls].sum %= p;
t[rs].lazy += lz; t[rs].lazy %= p;
t[rs].sum += lz * (r - mid); t[rs].sum %= p;
lz = t[x].laz; t[x].laz = 1;//乘法是这样吧
t[ls].laz = t[ls].laz * lz % p;
t[ls].sum = t[ls].sum * lz % p;
t[rs].laz = t[rs].laz * lz % p;
t[rs].sum = t[rs].sum * lz % p;
}
*/
void modify(int x, int l, int r, ll d, ll k)
{
t[x].laz = (t[x].laz * k) % p;
t[x].lazy = (t[x].lazy * k) % p;//加法的标记也经过乘法计算
t[x].lazy = (t[x].lazy + d) % p;
t[x].sum = (t[x].sum*k+(r-l+1)*d) % p;
}
void pushdown(int x, int l, int r)
{
int mid = (l + r) >> 1;
modify(x<<1, l, mid, t[x].lazy, t[x].laz);
modify(x<<1|1, mid+1, r, t[x].lazy, t[x].laz);
t[x].lazy = 0;
t[x].laz = 1;
}
void build(int x, int l, int r)
{
t[x].laz = 1;
if(l == r)
{
t[x].sum = a[l] % p;
return;
}
int mid = (l + r) >> 1;
build(x<<1, l, mid);
build(x<<1|1, mid+1, r);
pushup(x);
}
void update(int x, int l, int r, int L, int R, ll d, ll k)
{
if(L <= l && r <= R)
{
//t[x].sum += (r - l + 1) * d; t[x].lazy += d;
//t[x].sum %= p; t[x].lazy %= p;
modify(x, l, r, d, k);
return;
}
//if(t[x].lazy || t[x].laz != 1) pushdown(x, l, r);
pushdown(x, l, r);
int mid = (l + r) >> 1;
if(L <= mid) update(x<<1, l, mid, L, R, d, k);
if(R > mid) update(x<<1|1, mid+1, r, L, R, d, k);
pushup(x);
}
/*
void modify(int x, int l, int r, int L, int R, ll d)
{
//printf("cmp %d %d and %d %d\n", L, l, r, R);
if(L <= l && r <= R)
{
//t[x].sum += (r - l + 1) * d; t[x].lazy += d;
t[x].sum = t[x].sum * d % p; t[x].laz = t[x].laz * d % p;
//printf("%d %d *%lld\n", l, r, d);
return;
}
//printf("???????\n");
if(t[x].lazy || t[x].laz != 1) pushdown(x, l, r);
int mid = (l + r) >> 1;
if(L <= mid) modify(x<<1, l, mid, L, R, d);
if(R > mid) modify(x<<1|1, mid+1, r, L, R, d);
pushup(x);
}
*/
ll query(int x, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return t[x].sum % p;//%p没必要吧?
}
//if(t[x].lazy || t[x].laz != 1) pushdown(x, l, r);
pushdown(x, l, r);
int mid = (l + r) >> 1; ll ans = 0;
if(L <= mid) ans += query(x<<1, l, mid, L, R), ans %= p;
if(R > mid) ans += query(x<<1|1, mid+1, r, L, R), ans %= p;
return ans;
}
}t;
int main()
{
n = read(); p = read();
for(int i=1; i<=n; i++) a[i] = read();
t.build(1, 1, n);
m = read();
for(int i=1; i<=m; i++)
{
int op = read();
if(op == 2)
{
int l = read(), r = read(); ll d = read();
//t.query(1, 1, n, l, r);//处理加法先PushDown?
/*
没用的,加法和乘法一起操作会寄的……
难道是我的PushDown顺序有问题??
*/
t.update(1, 1, n, l, r, d, 1);
}
else if(op == 3)
{
int l = read(), r = read();
ll ans = t.query(1, 1, n, l, r);
printf("%lld\n", ans);
}
else
{
int l = read(), r = read(); ll d = read();
t.update(1, 1, n, l, r, 0, d);
//for(int j=1; j<=n; j++) printf("%lld ", t.query(1, 1, n, j, j));
//printf("\n");
}
}
return 0;
}
确实需要两个标记,一个乘法标记一个加法标记,但是两种计算并不能完全分开。加法标记在进行下移或者修改之前,需要先被它自己的乘法标记修改,也就是说加法的标记也需要经过乘法计算,因为乘法的优先级高于加法,只要涉及这个区间,这个区间的任意位置存在乘法标记,就必须先计算,sum的计算也是乘法在前。
看起来改了很多,但是这个pushdown和原来就是一个意思,貌似也没有改多少。
B.可持久化平衡树
但是借鉴了%%%小粉兔用权值树状数组
本来应该总结几句的,但是还有好多好多内容等着我复习,暂且先使用注释吧
还有贴图

code
/*
大开眼界,原来树状数组真的可以实现平衡树!!!
%%%小粉兔
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 3;
const int INF = 0x7fffffff;
int n, ans[maxn], a[maxn], b[maxn], c[maxn];
int opt[maxn], v[maxn], N;
struct edge
{
int to, nxt;
}e[maxn];
int len, head[maxn];
void add_e(int x, int y)
{
e[++len].to = y; e[len].nxt = head[x];
head[x] = len;
}
void add(int x, int d)
{
while(x <= N)//注意是<=
{
c[x] += d;
x += x & -x;
}
}
int query(int x)
{
int ans = 0;
while(x)
{
ans += c[x];
x -= x & -x;
}
return ans;
}
//由离散化前的次序x找到离散化后的次序p,x和p都不包含哨兵
//可是排名定义为比这个数小的数+1,包含哨兵的话就是严格比这个数小的数的个数
//就算x不能减到0,我得到的全是严格小于的p,最终都要+1的
//p+1,因为求得的p是严格小于,严格小于又是因为x不含哨兵以及下文if条件
int fnd(int x)
{
int p = 0;
for(int j=1<<18; j; j>>=1)
{
/*
p在增大,但是由于p|j,j在减小,lowbit(p)在减小
所以包含的区间长度在减小,前缀和在逐渐向右拓展……
直至x可能还有剩余(没有二进制拆分x),但已经不足以让区间拓展一个位置
也就是前缀和加上c[p+1](p+1这一个离散值出现的次数)都会超出x
p就是使(离散化后的)b[p]<=(离散化前的)b[x]的最大值
*/
if((p|j)<=N && c[p|j]<=x) x -= c[p|=j];
/*
一眼看去只有这一行难以理解,其实是因为对树状数组了解太浅
只知用法却忘记了树状数组这个数组本身的含义
c[x]存储的本来就是一段区间和!!
我们要求的是,前缀和等于x的树状数组的长度
树状数组的前缀和sum[pos]为小于等于当前位置(pos)的所有数的个数(去重前)
而pos是对应于树状数组的位置,更是对应于离散化b数组的位置,它们的下标取同一含义
所以我得到了位置pos,也就是从离散化前的顺序找到离散化后的顺序
*/
}
return p;
}
void dfs(int u, int o, int x)
{
int ext = 1;//要删的数是否存在exist,好严谨啊
if(o == 1) add(x, 1);//x是已经加哨兵离散化过的,但是4对应的次序不是
if(o == 2)
{
if(query(x) == query(x-1)) ext = 0;
else add(x, -1);
}
if(o == 3) ans[u] = query(x-1);//因为query包含x自己所以-1,不+1是因为有-INF
if(o == 4) ans[u] = b[fnd(x)+1];
//query(x-1)先找到当前排名,-1是前驱的排名,再由排名1找到排名2
if(o == 5) ans[u] = b[fnd(query(x-1)-1)+1];
if(o == 6) ans[u] = b[fnd(query(x))+1];
for(int i=head[u]; i; i=e[i].nxt)
{
dfs(e[i].to, opt[e[i].to], a[e[i].to]);
}
//回溯
if(o == 1) add(x, -1);
if(o == 2 && ext) add(x, 1);
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d%d%d", &v[i], &opt[i], &a[i]);
if(opt[i] != 4) b[++N] = a[i];
}
b[++N] = -INF; b[++N] = INF;
sort(b+1, b+N+1);
N = unique(b+1, b+N+1)-b-1;
for(int i=1; i<=n; i++)
{
add_e(v[i], i);
if(opt[i] != 4) a[i] = lower_bound(b+1, b+N+1, a[i])-b;
}
add(1, 1); add(N, 1);
dfs(0, 0, 0);
for(int i=1; i<=n; i++)
{
if(opt[i] > 2) printf("%d\n", ans[i]);
}
return 0;
}
C.约数个数
给定 n 个正整数 ,请你输出这些数的乘积的约数个数,答案对 取模。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含一个整数 。
输出格式
输出一个整数,表示所给正整数的乘积的约数个数,答案需对 取模。
数据范围
,
输入样例:
3
2
6
8
输出样例:
12
被我给水过去了qwq?!
code
/*
(c[i]+1)的连乘积
对每个数质因数分解
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 4;
const int mod = 1e9 + 7;
int n, ans=1, p[maxn], c[maxn], m, v[maxn];
/*void divide(int n)
{
m = 0;
for(int i=2; i<=sqrt(n); i++)
{
if(n % i == 0)
{
p[++m] = i; c[m] = 0;
while(n % i == 0) n /= i, c[m]++;
}
}
if(n > 1) p[++m] = n, c[m] = 1;
for(int i=1; i<=m; i++)
{
ans = (ll)ans * (c[i]+1) % mod;
//printf("%d ", c[i]);
}
//printf("\n");
}*/
//分解没错,但是分开分解就错了
//所以要把结果统计到同一个质数上!
void primes(int n)
{
for(int i=2; i<=n; i++)
{
if(v[i] == 0) {v[i] = i; p[++m] = i;}
for(int j=1; j<=m; j++)
{
if(p[j] > v[i] || p[j] > n/i) break;
v[i*p[j]] = p[j];
}
}
}
void divide(int n)
{
for(int i=1; i<=m; i++)
{
while(n % p[i] == 0) n /= p[i], c[i]++;
}
//如果n自己就是质数,那么它大于1e6筛选范围了,侥幸一下
//居然,给我水过去了
if(n > 1) ans = (ll)ans * 2 % mod;
}
int main()
{
scanf("%d", &n);
primes(1e6);
for(int i=1; i<=n; i++)
{
int x; scanf("%d", &x);
divide(x);
}
for(int i=1; i<=m; i++)
{
ans = (ll)ans * (c[i]+1) % mod;
}
printf("%d", ans);
return 0;
}
7-10 数列计数
问题描述
先给定两个长度为的数组,,问有多少长度为的数组
能够满足
由于答案可能很大,请输出答案对998244353取模的结果
注: 同
输入:
第一行包含一个整数,
第二行包含 个用空格分隔的整数
第二行包含 个用空格分隔的整数
输出:
输出一个整数,表示答案对998244353取模的结果
样例输入:
5
8 6 10 4 5
7 9 8 10 5
样例输出
96
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB
无论多少遍也都是天天在贺吗……那就相信未来吧
code
/*
关于7-10,是每一项C(a, b)都是奇数,然后把a[i]二进制拆了,
根据n & m == m的组合数奇偶性判定求b的个数吗?感觉偶数比奇数好算,
所以做减法?(数学真的很烂了根本不敢写……)
以上搞错了,这么判定得到奇数;而且,&运算你是不是和|搞混了,糟糕……
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 3;
const int mod = 998244353;
int n, a[maxn], l[maxn], ans=1;
//int t, t1, b[33], b1[33];
//贺(
int query(int x, int lim)
{
int sum = 0, ans = 1;
int cnt[32] = {0};
for(int i=0; i<30; i++) cnt[i+1] = cnt[i] + (x>>i&1);
//在第i位更低位有几个1,不包括第i位
for(int i=30; i>=0; i--)
{
int t = x >> i & 1;
if(!t) continue;
int tmp = sum + (1 << i);
if(tmp > lim) continue;
/*
每一次加上把这一位填0的情况数,然后把这一位填上1再往下
如果这一位本来不能填1,那就留待以后再算,每次加上的都是以后不再考虑的
所以和这一位有关的以后不考虑的情况已经加上了,这一位已经填好了
就像这一位从一开始就不一样
*/
sum = tmp; ans = (ans + (1<<cnt[i])) % mod;
/*
就算后来的位不会再有1,现在填上0的这一种情况也需要记录,因为接下来这一位就填上1了
那么我填这么多1的情况怎么计算啊……
不对,加上的情况数都是不包含当前位的之后的自由位,那么下文没有自由位了……
tmp就是卡着上限往上填
cnt[i]是,i上填0的话,剩余的自由位的个数,那自由位有0个的话就是剩下的最后一种情况了
只有自由位对答案有贡献,非自由位在那个1里都加上了,不可能加不上,除非x是0,那就真是0了
*/
}
return ans;
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=1; i<=n; i++) scanf("%d", &l[i]);
for(int i=1; i<=n; i++)
{
ans = (ll)ans * query(a[i], l[i]) % mod;
}
printf("%d", ans);
return 0;
}
P1941 [NOIP 2014 提高组] 飞扬的小鸟
(感觉至少应该会做部分分,可惜没有,Catherine根本就没学会动态规划)
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 4;
const int inf = (1 << 30);
int n, m, K, u[maxn], d[maxn], f[maxn][1007];
struct node
{
int pos, u, d;
bool operator < (const node &T) const
{
return pos < T.pos;
}
}p[maxn];
int main()
{
//让我看看怎么朴素
scanf("%d%d%d", &n, &m, &K);
for(int i=0; i<=n-1; i++) scanf("%d%d", &u[i], &d[i]);
for(int i=1; i<=K; i++)
{
scanf("%d%d%d", &p[i].pos, &p[i].d, &p[i].u);
}
sort(p+1, p+1+K);
for(int i=0; i<=n; i++)
{
for(int j=0; j<=m; j++) f[i][j] = inf;
}
for(int i=1; i<=m; i++) f[0][i] = 0;
int nxtp = 1;
for(int i=1; i<=n; i++)
{
int mi = 1, mx = m;
if(p[nxtp].pos == i)
{
mi = p[nxtp].d + 1;
mx = p[nxtp].u - 1;
nxtp++;
}
for(int j=1; j<=mx; j++)
{
for(int k=j-u[i-1]; k<=m; k++)
{
if(k>j-u[i-1] && j<m) break;
//把当前的f[i][k]用来转移是优化的关键
//因为用到了f[i][k],所以不能考虑当前位置的柱子,也不能先考虑当前位置的下降
if(k>=1) f[i][j] = min(f[i][j], min(f[i-1][k], f[i][k])+1);
}
}
for(int j=mi; j<=mx; j++)
{
if(j+d[i-1]<=m) f[i][j] = min(f[i][j], f[i-1][j+d[i-1]]);
}
for(int j=1; j<=mi-1; j++) f[i][j] = inf;
/*for(int j=mi; j<=mx; j++)
{
if(j == m)
{
for(int k=1; k<=m; k++)
{
f[i][j] = min(f[i][j], f[i-1][k]+((m-k)/u[i-1])+(((m-k)%u[i-1]||k==m)?1:0));
}
}
else
{
for(int k=1; j-k*u[i-1]>=0; k++)
{
f[i][j] = min(f[i][j], f[i-1][j-k*u[i-1]]+k);
}
}
if(j+d[i-1]<=m) f[i][j] = min(f[i][j], f[i-1][j+d[i-1]]);
}*/
}
int ans = inf;
bool flag = false;
for(int i=n; i>=1; i--)
{
for(int j=1; j<=m; j++)
{
if(f[i][j] != inf)
{
flag = true;
ans = min(ans, f[i][j]);
}
}
if(flag)
{
if(i == n)
{
printf("1\n%d", ans);
exit(0);
}
else
{
for(int o=K; o>=1; o--)
{
if(i >= p[o].pos)
{
printf("0\n%d", o);
exit(0);
}
}
}
}
}
printf("0\n0");
return 0;
}
时光花火,水月星辰

浙公网安备 33010602011771号