文章目录
- T1:单峰排列
- 题目
- 题解
- code
- T2:历史研究
- 题目
- 题解
- code
- T3:大魔法师
- 题目
- 题解
- code
我可能这辈子都更不出来狂欢赛5了,先咕咕
T1:单峰排列
题目
一个n的全排列A[i]是单峰的,当且仅当存在某个x使得A[1]<A[2]<…<A[x]>A[x+1]>…> A[n]
例如,对于9的全排列,125798643是一个单峰排列,123456789也是一个单峰排列,但356298741就不是
试求n的单峰全排列的个数
输入格式
输入一个数n
输出格式
输出n的全排列中单峰排列的个数。
由于这个数可能很大,因此你只需要输出它mod 1234567的值。
样例
输入样例:
3
输出样例:
4
样例说明 共有以下4种方案: 123 132 231 321
数据范围与提示
n<=2 000 000 000
题解
暴力打表找到规律2n−12^{n-1}2n−1
简单证明一下:
我们把最大值即最高峰固定后,剩下的n−1n-1n−1个数会有两种选法,在最大值左边或右边2n−12^{n-1}2n−1
理论上还要乘以一个阶乘,但是我们发现要成为单峰的话,同在最大值左边的数相对位置是固定的,是不能进行乱排的,证毕!!
code
#include <cstdio>
#define mod 1234567
#define LL long long
int n;
LL qkpow ( LL x, LL y ) {LL ans = 1;while ( y ) {if ( y & 1 )ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}
int main() {scanf ( "%d", &n );LL ans = qkpow ( 2, n - 1 );printf ( "%lld", ans % mod );return 0;
}
T2:历史研究
题目
IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件
事件有种类之分,第i天(1<=i<=N)发生的事件的种类用一个整数XiX_iXi表示,XiX_iXi 越大,事件的规模就越大。
JOIJOI教授决定用如下的方法分析这些日记:
选择日记中连续的一些天作为分析的时间段
事件种类t的重要度为t*(这段时间内重要度为t的事件数)
计算出所有事件种类的重要度,输出其中的最大值 现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
输入输出格式
输入格式
第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次
接下来一行N个空格分隔的整数X1...XNX_1...X_NX1...XN表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数AiA_iAi 和BiB_iBi表示第i次询问的区间为[Ai,Bi][A_i,B_i][Ai,Bi]
输出格式
输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度
输入输出样例
输入:
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
输出:
9
8
8
16
16
数据范围
1<=N<=105,1<=Q<=105,1<=Xi<=109(1<=i<=N)1<=N<=10^5,1<=Q<=10^5, 1<=X_i<=10^9 (1<=i<=N)1<=N<=105,1<=Q<=105,1<=Xi<=109(1<=i<=N)
题解
这道题首先肯定能反应是莫队,但是答案告诉你TTT了,所以我们要学习一个新的回滚莫队
假设要查询[l,r][l,r][l,r]区间,按照平时我们要移动curl,currcurl,currcurl,curr,但是我们转变想法对于lll同在一个分块的查询,每一次都把curlcurlcurl回拨到这一区域的最后处然后移动到lll
但是这张图是错的,你看出来了吗?因为同在一个块的操作rrr一定是递增的,所以上图中的第二根红线应该在最后一个竖黑线的右边
具体操作细节在code中有体现
code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define maxn 100005
struct node {int l, r, id;
}query[maxn];
int n, q, T;
int block[maxn];
LL result;
LL a[maxn], val[maxn], cnt[maxn], tot[maxn], ans[maxn]; bool cmp ( node x, node y ) {//以块为第一关键字,右端点为第二关键字 return block[x.l] == block[y.l] ? x.r < y.r : block[x.l] < block[y.l];
}void add ( int x ) {cnt[a[x]] ++;result = max ( result, cnt[a[x]] * val[a[x]] );//当规模为i的时间多了一件,重要度就多了i
}
void remove ( int x ) {cnt[a[x]] --;
}int main() {scanf ( "%d %d", &n, &q );T = sqrt ( n );for ( int i = 1;i <= n;i ++ ) {scanf ( "%lld", &a[i] );block[i] = ( i - 1 ) / T + 1;val[i] = a[i];}int num = block[n];//离散化,1e9最多只会变成1e5,但是规模在计算时要用原来的,所以不能直接替代 sort ( val + 1, val + n + 1 );int len = unique ( val + 1, val + n + 1 ) - val - 1;for ( int i = 1;i <= n;i ++ )a[i] = lower_bound ( val + 1, val + len + 1, a[i] ) - val; for ( int i = 1;i <= q;i ++ ) {scanf ( "%d %d", &query[i].l, &query[i].r );query[i].id = i;}sort ( query + 1, query + q + 1, cmp );int i = 1;//已经处理到哪一个query操作了 for ( int id = 1;id <= num;id ++ ) {//依次处理每一块int tail = min ( n, id * T );//有可能块数乘以大小反而炸了n int curl = tail + 1, curr = tail;result = 0;memset ( cnt, 0, sizeof ( cnt ) );//每一个块都重新开始,与其他块无瓜for ( ;block[query[i].l] == id;i ++ ) {//属于同一块的每个操作一定是右端点递增的 int l = query[i].l, r = query[i].r;if ( block[query[i].l] == block[query[i].r] ) {//整一个询问都在该块中,暴力求解 LL sum = 0;//不能用result,该次询问根本没用到块以外的元素 for ( int k = l;k <= r;k ++ )tot[a[k]] = 0;//注意不是对tot[k]清零 for ( int k = l;k <= r;k ++ ) {tot[a[k]] ++;sum = max ( sum, tot[a[k]] * val[a[k]] );}ans[query[i].id] = sum;continue;}while ( curr < r )add ( ++ curr );//curr贡献已经算过,所以先加后算,curl同理 LL tmp = result;//r一定是递增的,记录下此时块的末尾到n的ans,方便下面的还原 while ( curl > l )add ( -- curl );ans[query[i].id] = result;while ( curl <= tail )//把curl拨回到下一个块的开头,表示没有一个元素,等到下一次移动到新的l remove ( curl ++ );result = tmp;//重置起始值}}for ( int i = 1;i <= q;i ++ )printf ( "%lld\n", ans[i] );return 0;
}
T3:大魔法师
题目
中二病患者 大魔法师小 L 制作了nnn个魔力水晶球,每个水晶球有水、火、土三个属性的能量值。小 L 把这 个水晶球在地上从前向后排成一行,然后开始今天的魔法表演。
我们用Ai,Bi,CiA_i,B_i,C_iAi,Bi,Ci分别表示从前向后第iii个水晶球(下标从 开始)的水、火、土的能量值。
小 L 计划施展mmm次魔法。每次,他会选择一个区间[l,r][l,r][l,r],然后施展以下3大类、7种魔法之一:
魔力激发:令区间里每个水晶球中特定属性的能量爆发,从而使另一个特定属性的能量增强。具体来说,有以下三种可能的表现形式:
火元素激发水元素能量:令Ai=Ai+BiA_i=A_i+B_iAi=Ai+Bi 。
土元素激发火元素能量:令Bi=Bi+CiB_i=B_i+C_iBi=Bi+Ci 。
水元素激发土元素能量:令Ci=Ci+AiC_i=C_i+A_iCi=Ci+Ai 。
需要注意的是,增强一种属性的能量并不会改变另一种属性的能量,例如Ai=Ai+BiA_i=A_i+B_iAi=Ai+Bi并不会使BiB_iBi增加或减少。
魔力增强:小 L 挥舞法杖,消耗自身vvv点法力值,来改变区间里每个水晶球的特定属性的能量。具体来说,有以下三种可能的表现形式:
火元素能量定值增强:令Ai=Ai+vA_i=A_i+vAi=Ai+v。
水元素能量翻倍增强:令Bi=Bi∗vB_i=B_i*vBi=Bi∗v 。
土元素能量吸收融合:令Ci=vC_i=vCi=v 。
魔力释放:小L将区间里所有水晶球的能量聚集在一起,融合成一个新的水晶球,然后送给场外观众。生成的水晶球每种属性的能量值等于区间内所有水晶球对应能量值的代数和。需要注意的是,魔力释放的过程不会真正改变区间内水晶球的能量。
值得一提的是,小 L 制造和融合的水晶球的原材料都是定制版的 OI 工厂水晶,所以这些水晶球有一个能量阈值998244353998244353998244353 。当水晶球中某种属性的能量值大于等于这个阈值时,能量值会自动对阈值取模,从而避免水晶球爆炸。
小 W 为小 L(唯一的)观众,围观了整个表演,并且收到了小 L 在表演中融合的每个水晶球。小 W 想知道,这些水晶球蕴涵的三种属性的能量值分别是多少
题解
首先大家都能想到线段树,我当时直接暴力线段树开搞,后来操作时写崩了,这个更改太给力
况且因为有懒标记的介入,a,b,ca,b,ca,b,c都会改了后再彼此影响,我们根本不知道先后顺序
那咱不用懒标记呗
那么怎么能将懒标记按顺序叠加呢?矩阵!!!
对于操作1,即乘上
(100110001)\begin{pmatrix} 1&0&0\\ 1&1&0\\ 0&0&1\\ \end{pmatrix} ⎝⎛110010001⎠⎞
操作2,乘上
(100010011)\begin{pmatrix} 1&0&0\\ 0&1&0\\ 0&1&1\\ \end{pmatrix} ⎝⎛100011001⎠⎞
操作3,乘上
(101010001)\begin{pmatrix} 1&0&1\\ 0&1&0\\ 0&0&1\\ \end{pmatrix} ⎝⎛100010101⎠⎞
操作4,我们发现怎么加常数?多给矩阵开一位(上面就不再次多余重复),乘上
(100001000010v001)\begin{pmatrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&1&0\\ v&0&0&1\\ \end{pmatrix} ⎝⎜⎜⎛100v010000100001⎠⎟⎟⎞
操作5,乘上
(10000v0000100001)\begin{pmatrix} 1&0&0&0\\ 0&v&0&0\\ 0&0&1&0\\ 0&0&0&1\\ \end{pmatrix} ⎝⎜⎜⎛10000v0000100001⎠⎟⎟⎞
操作6,乘上
(10000100000000v1)\begin{pmatrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&0&0\\ 0&0&v&1\\ \end{pmatrix} ⎝⎜⎜⎛10000100000v0001⎠⎟⎟⎞
然后矩阵的懒标记就可以直接叠加了,最后送大家五句忠告
1、此做法常数大,写的不好看会100–>45
2、矩阵从0开始,别从1,某朋友MLE
3、矩阵逼着4开,多开1,亲测TLE
4、longlong比int取模会慢一倍,所以你懂的
5、取模本身也很慢,能不取模就不取模
code
#include <cstdio>
#include <cstring>
#define maxn 250005
const int mod = 998244353;
struct Matrix {int c[4][4];
//开成c[5][5]就T了一个点,因为我们每次都用了memset,多清了一行,浪费时间 Matrix () {memset ( c, 0, sizeof ( c ) );}
}unit, ret, A[maxn], tag[maxn << 2], tree[maxn << 2];
//unit是单位矩阵 void read ( int &x ) {x = 0;char s = getchar();while ( s < '0' || s > '9' )s = getchar();while ( '0' <= s && s <= '9' ) {x = ( x << 1 ) + ( x << 3 ) + ( s - '0' );s = getchar();}
}int add ( int x, int y ) {x = 1ll * x + y;return x >= mod ? x - mod : x;
}Matrix operator + ( Matrix x, Matrix y ) {Matrix ans;for ( int i = 0;i < 4;i ++ )ans.c[0][i] = add ( x.c[0][i], y.c[0][i] );return ans;
}void build ( int t, int l, int r ) {tag[t] = unit;if ( l == r ) {tree[t] = A[l];return;}int mid = ( l + r ) >> 1;build ( t << 1, l, mid );build ( t << 1 | 1, mid + 1, r );tree[t] = tree[t << 1] + tree[t << 1 | 1];
}void MakeMark ( int t, Matrix f ) {Matrix ans;for ( int i = 0;i < 4;i ++ )for ( int j = 0;j < 4;j ++ )if ( f.c[i][j] )ans.c[0][j] = add ( ans.c[0][j], 1ll * tree[t].c[0][i] * f.c[i][j] % mod );tree[t] = ans;ans = Matrix();for ( int i = 0;i < 4;i ++ )for ( int j = 0;j < 4;j ++ )if ( tag[t].c[i][j] ) //稀疏矩阵优化 for ( int k = 0;k < 4;k ++ )if ( f.c[j][k] )ans.c[i][k] = add ( ans.c[i][k], 1ll * tag[t].c[i][j] * f.c[j][k] % mod );tag[t] = ans;
}void pushdown ( int t ) {MakeMark ( t << 1, tag[t] );MakeMark ( t << 1 | 1, tag[t] );tag[t] = unit;//注意清除标记就是变成单位矩阵不是0
}void modify ( int t, int l, int r, int L, int R, Matrix f ) {if ( L <= l && r <= R ) {MakeMark ( t, f );return;}int mid = ( l + r ) >> 1;pushdown ( t );if ( L <= mid )modify ( t << 1, l, mid, L, R, f );if ( mid < R )modify ( t << 1 | 1, mid + 1, r, L, R, f );tree[t] = tree[t << 1] + tree[t << 1 | 1];
}Matrix query ( int t, int l, int r, int L, int R ) {if ( L <= l && r <= R )return tree[t];pushdown ( t );int mid = ( l + r ) >> 1;if ( R <= mid )return query ( t << 1, l, mid, L, R );else if ( mid < L )return query ( t << 1 | 1, mid + 1, r, L, R );elsereturn query ( t << 1, l, mid, L, R ) + query ( t << 1 | 1, mid + 1, r, L, R );
}int main() {int n, m;read ( n );for ( int i = 1;i <= n;i ++ ) {for ( int j = 0;j < 3;j ++ )read ( A[i].c[0][j] );A[i].c[0][3] = 1;}for( int i = 0;i < 4;i ++ )unit.c[i][i] = 1;build ( 1, 1, n );read ( m );int opt, l, r, v;for ( int i = 1;i <= m;i ++ ) {read ( opt );read ( l );read ( r );if ( 4 <= opt && opt <= 6 )read ( v );if ( opt == 7 ) {Matrix ans = query ( 1, 1, n, l, r );printf ( "%d %d %d\n", ans.c[0][0], ans.c[0][1], ans.c[0][2] );continue;}ret = unit;//重置成单位矩阵再进行修改操作 switch ( opt ) {case 1 : ret.c[1][0] = 1;break;case 2 : ret.c[2][1] = 1;break;case 3 : ret.c[0][2] = 1;break;case 4 : ret.c[3][0] = v;break;case 5 : ret.c[1][1] = v;break;case 6 : ret.c[2][2] = 0, ret.c[3][2] = v;break;}modify ( 1, 1, n, l, r, ret );}return 0;
}