[多校联考-西南大学附中]切面包(线段树/概率与期望)+ Slow Path Finding Algorithm(拓扑排序/DP)+ 分数转化(数论)

文章目录

  • T1:分数转换
    • 题目
    • 题解
    • 代码实现
  • T2:Slow Path Finding Algorithm
    • 题目
    • 题解
    • 代码实现
  • T3:切面包
    • 题目
    • 题解
    • 代码实现

T1:分数转换

题目

Time limit: 1.5 seconds
Memory limit: 512 megabytes

给定一个十进制小数,请你将它转换为分数。
给定的小数可能有循环节,用 [] 表示,如 0.12[3] 表示 0.1233333…

Input
输入文件包含多组测试数据。
第一行一个整数 T (1 ≤ T ≤ 3 · 105 ),表示测试数据的组数。
每组测试数据一行一个字符串 s i ,表示需要转换的小数,保证 1 ≤ |s i | ≤ 18,且保证由符号部分,整数
部分、小数点、小数部分组成,整数部分没有前导零,如果小数部分有循环节,则循环节一定在末尾且
循环节的最后一位非零。
Output
每组测试数据输出一行一个分数 p/q,其中 p, q 为整数且 q ≥ 1, gcd(p,q) = 1

Scoring
本题共有 3 个测试点
测试点 1 (30 分):保证给定小数没有循环节
测试点 2 (30 分):保证给定小数的整数部分为 0,且循环节是整个小数部分
测试点 3 (40 分):无特殊限制

Example
frac.in
5
-0.0
0.[3]
1926.0[817]
1.00
-123.456
frac.out
0/1
1/3
19241557/9990
1/1
-15432/125

题解

旁边的dalao都说这是一道小学奥数题。。。
在这里插入图片描述
而我则是在考场上用尽了计算机,看了多少数据,才发现了规律

简略题解如下:
对于测试点 1,直接写成 p/10k 后约分即可
对于测试点 2,假设循环节为 t,长度为 k,则可以列出方程 10kx = x + t,解出来即可
对于测试点 3,只需在测试点 2 的基础上多一些细节处理


接下来,就让大家一起来找规律吧!!!
在这里插入图片描述在这里插入图片描述
可以多打几组表看看,会发现规律如下
循环节其实就是分子(舍掉前导零),而循环节的个数就对应分母的9的个数
在这里插入图片描述


这个规律发现后,整道题就变成了一个水题
1.首先对于没有循环节的输入,如

123.1025

大家肯定都知道,将其转化为1231025/100001231025/100001231025/10000,然后在用gcd去约分化到最简即可,不再过多赘述


2.只有整数和循环节的输入,如

123.[7851]

就先把循环小数通过上面的规律化成分数7851/99997851/99997851/9999,在把整数与之同分再化简,也是gcd去完成


.3.整数,小数,循环节都有的输入,如样例

1926.0[817]

就先把整数与小数进行合并同分再化简,这样就又转换成了情况2,同样的处理即可

也就是说,先把输入处理成(19260+[817])/10(19260+[817])/10(19260+[817])/10,再变成了(19260+817999)/10(19260+\frac{817}{999})/10(19260+999817)/10
变成1926+81799901926+\frac{817}{9990}1926+9990817再同分化简,就ok了
在这里插入图片描述
代码里有一些细节处理,这里就不再一一解释,大家可以自行看懂。。。


代码实现

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream> 
using namespace std;
#define LL long long
#define MAXN 20
int T;
char s[MAXN];LL gcd ( LL x, LL y ) {if ( ! y )return x;return gcd ( y, x % y );
}int main() {scanf ( "%d", &T );while ( T -- ) {scanf ( "%s", s );int len = strlen ( s );bool flag1 = 0, flag2 = 0;int digit = 0;LL cnt1 = 0, cnt2 = 0, cnt3 = 0;LL tot1 = 0, tot2 = 0, tot3 = 0;while ( digit < len && s[digit] == '-' ) {flag1 = 1;digit ++;}while ( digit < len && s[digit] != '.' ) {cnt1 = ( cnt1 << 3 ) + ( cnt1 << 1 ) + ( s[digit] - '0' );digit ++; tot1 ++;}if ( s[digit] == '.' )digit ++;while ( digit < len && s[digit] != '[' ) {cnt2 = ( cnt2 << 3 ) + ( cnt2 << 1 ) + ( s[digit] - '0' );digit ++;tot2 ++;}if ( s[digit] == '[' ) {digit ++;flag2 = 1;}while ( digit < len && s[digit] != ']' ) {cnt3 = ( cnt3 << 3 ) + ( cnt3 << 1 ) + ( s[digit] - '0' );digit ++;tot3 ++;}LL N = ( LL ) pow ( 10, tot2 );LL d, fenzi, fenmu;if ( tot2 != 0 ) {d = gcd ( cnt1 * N + cnt2, N );fenzi = ( cnt1 * N + cnt2 ) / d;fenmu = N / d;}else {fenzi = N;if ( cnt1 == 0 )fenzi = 0;fenmu = 1;}if ( cnt1 == 0 && cnt2 == 0 && cnt3 == 0 )flag1 = 0;if ( flag1 )printf ( "-" );if ( ! flag2 ) {printf ( "%lld/%lld\n", fenzi, fenmu );continue;}N = ( LL ) ( pow ( 10, tot3 ) ) - 1;N = N * ( LL ) ( pow ( 10, tot2 ) ); d = gcd ( cnt3, N );LL newfenzi = cnt3 / d;LL newfenmu = N / d;if ( newfenmu == fenmu ) {d = gcd ( fenzi + newfenzi, fenmu );printf ( "%lld/%lld\n", ( fenzi + newfenzi ) / d, fenmu / d );}else {d = gcd ( newfenmu, fenmu );LL lcm = newfenmu * ( fenmu / d );fenzi = fenzi * ( lcm / fenmu );newfenzi = newfenzi * ( lcm / newfenmu );d = gcd ( newfenzi + fenzi, lcm );printf ( "%lld/%lld\n", ( fenzi + newfenzi ) / d, lcm / d );}}return 0;
}

T2:Slow Path Finding Algorithm

题目

Time limit: 6 seconds
Memory limit: 512 megabytes

小 H 今天学习了「缓慢的路径寻找算法」 ,下课后便准备找一道题练习一下。题目是这样的:给定一张
有向图,每条边上都有一个小写英文字母,小 H 需要寻找一条路径使得路径上出现最多的字母的出现次
数最大。然而小 H 想了很久也只会 |V | = 1 的情形,于是他找到了你,请你帮他解决这个问题

Input
输入文件包含多组测试数据。
第一行一个整数 T (1 ≤ T ≤ 105 ),表示测试数据的组数。
每组测试数据的第一行两个整数 n, m (1 ≤ n ≤ 105 , 0 ≤ m ≤ 2 · 105 ),分别表示有向图的点数和边数
接下来 m 行,每行两个整数 u i , v i (1 ≤ u i ,v i ≤ n) 和一个小写英文字母 c i ,表示从 u i 到 v i 有一条有向
边,上面的字母为 c i 。
保证
∑n ≤ 10 6 ,∑m ≤ 2 · 10 6 。
Output
对于每组测试数据,如果路径上出现最多的字母的出现次数可以是任意大,输出一行 -1
否则,在第一行依次输出一个整数 ans,一个字母 c 和一个整数 k (1 ≤ k ≤ n),依次表示路径上出现最
多的字母的出现次数,达到最多出现次数的字母以及路径上的点数
第二行输出 k 个整数 p 1 ,p 2 ,…,p k (1 ≤ p i ≤ n),表示这条路径依次经过的点
如果有多条满足条件的路径,输出任意一条

Scoring
本题共有 5 个测试点,每个测试点 20 分。
测试点 1:n,m ≤ 5。
测试点 2:每组测试数据中的 c i 均相同。
测试点 3:T,n,m ≤ 100。
测试点 4:保证图中不存在环。
测试点 5:无特殊限制。

Example
spfa.in
3
1 0
1 1
1 1 a
4 6
1 2 i
1 3 a
1 4 k
2 3 i
2 4 o
3 4 i
spfa.out
0 a 1
1
-1
3 i 4
1 2 3 4

Note
在第一组数据中,只有一个点,没有边,所以唯一的路径就是 [1],其中每个字母的出现次数都是 0。
在第二组数据中,有一个点和它到自己的一条边,只需要选择路径 [1,1,1,…],就能使得字母 a 出现任
意多次

题解

首先我们能知道如何判断-1的情况,如果一个字母能出现任意多次,证明有一条路可以一直重复走这一条边,那么图中肯定是有环的,所以我们可以用拓扑排序找环


处理了-1的情况后,再来思考对于这种树上路径求极值,很容易就想到DPDPDP
DP[i][j]DP[i][j]DP[i][j]表示走到i号点为止,字母j出现的最多次数

而这里我们采取用父亲更新儿子节点的值,原因如下:在这里插入图片描述
1.我们不知道真正的起点到底是谁,只能知道目前的终点为i
2.如果用父亲更新儿子,就可以在拓扑排序找环的时候就一起处理了

所以状态转移方程式如下:v是u的一个儿子节点,c是这条边上的字母
DP[v][j]=max(DP[v][j],DP[u][j]+(c==j))DP[v][j]=max(DP[v][j],DP[u][j]+(c==j))DP[v][j]=max(DP[v][j],DP[u][j]+(c==j))


最后就是路径输出问题了,其实可以在DP的时候,顺便把DP[v][j]DP[v][j]DP[v][j]状态下取最大值时所指向的父亲节点记录一下就可以了

这里在找的时候顺序会从终点找到起点,所以我们可以用stack栈来后进先出,把路径倒着输出
也可以vector存储后用一个翻转函数reverse输出也可以

代码实现

#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std; 
#define MAXN 100005
queue < int > q;
stack < int > path;
vector < vector < pair < int, int > > > G ( MAXN );
int T, n, m;
int d[MAXN];
int dp[MAXN][30], f[MAXN][30];int main() {scanf ( "%d", &T );while ( T -- ) {scanf ( "%d %d", &n, &m );for ( int i = 1;i <= n;i ++ ) {G[i].clear();d[i] = 0;for ( int j = 0;j < 26;j ++ )dp[i][j] = f[i][j] = 0;}for ( int i = 1;i <= m;i ++ ) {int u, v;char c;scanf ( "%d %d %c", &u, &v, &c );G[u].push_back( make_pair ( v, c - 'a' ) );d[v] ++;}int tot = 0;for ( int i = 1;i <= n;i ++ )if ( ! d[i] ) {q.push ( i );tot ++;}while ( ! q.empty() ) {int t = q.front();q.pop();for ( int i = 0;i < G[t].size();i ++ ) {int v = G[t][i].first;int c = G[t][i].second;for ( int j = 0;j < 26;j ++ )if ( dp[t][j] + ( c == j ) > dp[v][j] ) {dp[v][j] = dp[t][j] + ( c == j );f[v][j] = t;}d[v] --;if ( ! d[v] ) {q.push ( v );tot ++;}}}if ( tot != n )printf ( "-1\n" );else {int result = -1, str, idx;for ( int i = 1;i <= n;i ++ )for ( int j = 0;j < 26;j ++ )if ( dp[i][j] > result ) {result = dp[i][j];str = j;idx = i;}while ( idx != 0 ) {path.push ( idx );idx = f[idx][str];}printf ( "%d %c %d\n", result, str + 'a', path.size() );while ( ! path.empty() ) {printf ( "%d ", path.top() );path.pop();}printf ( "\n" );}}return 0;
}

它终于来了

T3:切面包

题目

Time limit: 1 second
Memory limit: 512 megabytes
小 H 有条一长长的面包。这条面包由 n 段组成。
每当有朋友到小 H 的家里玩,小 H 就会切下这块面包的一段,分享给朋友们。之后,小 H 又会重新制
作和之前完全一样的若干段面包填充切下的部分。
面包被切下时,有可能会裂开,第 i 段面包被切下时裂开的概率为pi998244352\frac{pi}{998244352}998244352pi 。小 H 想让朋友尽可能开
心,于是会把裂开的面包自己吃掉,而用没有裂开的面包招待朋友。小 H 特别喜欢分块,因此,如果小
H 有 x 段连续的没有裂开的面包,则朋友的开心度为 x2
有时,小 H 也会对一段面包进行加工,加工会改变面包裂开的概率
你需要在每次小 H 切蛋糕之前,回答他朋友的期望开心度是多少

Input
第一行两个整数 n, m (1 ≤ n,m ≤ 105),分别表示面包的长度和时间的次数。
第二行 n 个整数 p 1 ,p 2 ,…,p n (0 ≤ pi ≤ 998244352),表示每段面包初始时裂开的概率。
接下来 m 行,每行表示一个事件:
1 x i q i (1 ≤ x i ≤ n, 0 ≤ q i ≤ 998244352)——表示小 H 加工了第 x i 段面包,加工后 p[x i] 变成了 q i
2 l i r i (1 ≤ l i ≤ r i ≤ n)——表示小 H 询问若切下 [l i ,r i ] 内的面包,朋友的期望开心度是多少
Output
对每个 2 事件,输出一个整数,表示朋友的期望开心度对 998244353 取模后的值

Scoring
本题共有 5 个测试点,每个测试点 20 分
测试点 1:1 ≤ n,m ≤ 20
测试点 2:p i = 499122176,没有 1 事件
测试点 3:没有 1 事件
测试点 4:1 ≤ n,m ≤ 2 · 103
测试点 5:无特殊限制

Example
divide.in
3 5
499122176 499122176 499122176
2 1 1
2 1 2
2 1 3
1 2 0
2 1 3
divide.out
499122177
249561089
748683266
1

Note

题解

简略题解如下:
对于测试点 1,直接枚举所有情况。时间复杂度 O(n + m2n)
对于测试点 2,答案只和长度有关,可以发现答案其实是长度的一个二次函数,直接计算即可
也可以通过递推求出。时间复杂度 O(n + m)
接下来我们需要进行一定的推导。引入辅助变量 xi,当第 i 段裂开时 xi = 1,否则 xi = 0。
连续段的数量可以看作初始的一段加上断开的连接处再减去删去的段数,即
1+∑i=1n−1(xi+xi+1−xixi+1)−∑i=1nxi=1+∑i=2n−1xi−∑i=1n−1xixi+11+\sum_{i=1}^{n-1}(x_i+x_{i+1}-x_ix_{i+1})-\sum_{i=1}^nx_i=1+\sum_{i=2}^{n-1}x_i-\sum_{i=1}^{n-1}x_ix_{i+1}1+i=1n1(xi+xi+1xixi+1)i=1nxi=1+i=2n1xii=1n1xixi+1
则段数的平方为:
1+∑2≤i<n∑2≤j<nxixj+∑1≤i<n∑1≤j<nxixi+1xjxj+1+2∑2≤i<nxi−2∑1≤i<nxixi+1−2∑2≤i<n∑1≤j<nxixjxj+11+\sum_{2≤i<n}\sum_{2≤j<n}x_ix_j+\sum_{1≤i<n}\sum_{1≤j<n}x_ix_{i+1}x_jx_{j+1}+2\sum_{2≤i<n}x_i-2\sum_{1≤i<n}x_ix_{i+1}-2\sum_{2≤i<n}\sum_{1≤j<n}x_ix_jx_{j+1}1+2i<n2j<nxixj+1i<n1j<nxixi+1xjxj+1+22i<nxi21i<nxixi+122i<n1j<nxixjxj+1
将上式用pip_ipi表示,得到:
1+(∑i=2n−1pi)2+3∑i=2n−1pi−∑i=2n−1pi2+(∑i=1n−1pipi+1)2+2∑i=1n−2pipi+1pi+2−2∑i=2n−2pipi+12pi+21+(\sum_{i=2}^{n-1}p_i)^2+3\sum_{i=2}^{n-1}p_i-\sum_{i=2}^{n-1}p_i^2+(\sum_{i=1}^{n-1}p_ip_{i+1})^2+2\sum_{i=1}^{n-2}p_ip_{i+1}p_{i+2}-2\sum_{i=2}^{n-2}p_ip_{i+1}^2p_{i+2}1+(i=2n1pi)2+3i=2n1pii=2n1pi2+(i=1n1pipi+1)2+2i=1n2pipi+1pi+22i=2n2pipi+12pi+2
−5∑i=1n−1pipi+1+2∑i=2n−1pi2pi+1+2∑i=1n−2pipi+12−∑i=1n−1pi2pi+12−2(∑i=2n−1pi)(∑i=1n−1pipi+1)+2p1p2+2pn−1pn-5\sum_{i=1}^{n-1}p_ip_{i+1}+2\sum_{i=2}^{n-1}p_i^2p_{i+1}+2\sum_{i=1}^{n-2}p_ip_{i+1}^2-\sum_{i=1}^{n-1}p_i^2p_{i+1}^2-2(\sum_{i=2}^{n-1}p_i)(\sum_{i=1}^{n-1}p_ip_{i+1})+2p_1p_2 + 2p_{n-1}p_n5i=1n1pipi+1+2i=2n1pi2pi+1+2i=1n2pipi+12i=1n1pi2pi+122(i=2n1pi)(i=1n1pipi+1)+2p1p2+2pn1pn
对于测试点 3,直接记录前缀和即可。时间复杂度 O(n + m)
对于测试点 4,暴力查询。时间复杂度 O(nm)
对于测试点 5,使用数据结构维护 pi,pi2,pipi+1,pi2pi+1,pipi+12,pi2pi+12,pipi+1pi+2,pipi+12pi+2p_i, p_i^2, p_ip_{i+1}, p_i^2p_{i+1}, p_ip_{i+1}^2, p_i^2p_{i+1}^2, p_ip_{i+1}p_{i+2}, p_ip_{i+1}^2p_{i+2}pi,pi2,pipi+1,pi2pi+1,pipi+12,pi2pi+12,pipi+1pi+2,pipi+12pi+2的和即可,时间复杂度 O(n + m lg n)。
在这里插入图片描述


接下来进入本蒟蒻的自己尽力详解版,我会尽自己的全力把这个递推式推导给大家看的
在这里插入图片描述


1+∑i=1n−1(xi+xi+1−xixi+1)−∑i=1nxi=1+∑i=2n−1xi−∑i=1n−1xixi+11+\sum_{i=1}^{n-1}(x_i+x_{i+1}-x_ix_{i+1})-\sum_{i=1}^nx_i=1+\sum_{i=2}^{n-1}x_i-\sum_{i=1}^{n-1}x_ix_{i+1}1+i=1n1(xi+xi+1xixi+1)i=1nxi=1+i=2n1xii=1n1xixi+1
首先对于这个等式,其实我们直接推右边来得更快
在这里插入图片描述
这个就是我们假设每个碎掉的面包把面包分成了多段,每两个碎掉的面包彼此之间不冲突
意思就是i面包没有和i+1面包一起碎掉,因为这样的话其实只相当于一段长度为i面包和i+1面包长度之和的面包把左右分成了两段,并非我们理想的如上图一样分成3段,i与i+1面包之前没有完整的面包
这就是1+∑i=2n−1xi1+\sum_{i=2}^{n-1}x_i1+i=2n1xi部分

然而这只是我们的理想状态,真正是可能多个碎的面包碎在一起,形成一个更长的碎面包,这个时候分的段数就少了,1和n的端点也可能是碎的,这就是−∑i=1n−1xixi+1-\sum_{i=1}^{n-1}x_ix_{i+1}i=1n1xixi+1,为什么只有两个呢?
想一想我们把i和i+1合并成了一个大的碎面包,如果i+2面包也碎了,就再与它合并,相当于两两合并
在这里插入图片描述


关于以上公式的平方展开式,相信大家都会,我就不再赘述,进入最难环节,我们一项一项地看这个展开式是如何一步一步合并成下面那一大堆东西的

这里用pip_ipi带换了xix_ixi,用事件代换01,这就导致出现了下面的公式
首先要明白两个概念

pi∗pi=pip_i*p_i=p_ipipi=pi  表示的意义:pi事件和pi事件同时发生的概率就是pi事件发生的概率
pi+pj≠pi∗pjp_i+p_j≠p_i*p_jpi+pj=pipj 表示的意义:pi事件发生的概率和pj事件发生的概率并不等于pi和pj事件同时发生的概率


①1就不变,直接往下移,好了我们已经处理好了16\frac{1}{6}61
在这里插入图片描述



+∑2≤i<n∑2≤j<nxixj+\sum_{2≤i<n}\sum_{2≤j<n}x_ix_j+2i<n2j<nxixj
这个如果代换成了事件p,那么这里面就一种情况是错误的,即pi∗pi=pip_i*p_i=p_ipipi=pi就不满足上述公式
我们得先把错误的概率减掉再加上真的概率,也就转化成了
+(∑i=2n−1pi)2−∑i=2n−1pi2+∑i=2n−1pi+(\sum_{i=2}^{n-1}p_i)^2-\sum_{i=2}^{n-1}p_i^2+\sum_{i=2}^{n-1}p_i+(i=2n1pi)2i=2n1pi2+i=2n1pi
在这里插入图片描述



+∑2≤i<n∑1≤j<nxixi+1xjxj+1+\sum_{2≤i<n}\sum_{1≤j<n}x_ix_{i+1}x_jx_{j+1}+2i<n1j<nxixi+1xjxj+1
与②同样的思想,这里面算错了i=j,i=j+1,j=i+1i=j,i=j+1,j=i+1i=j,i=j+1,j=i+1三种情况,在这里要明白i=j+1,j=i+1i=j+1,j=i+1i=j+1,j=i+1本质上是一致的,因为i和j只是我们的一个循环变量名罢了,我们换一下也是不会影响的,所以下面的j,我就都写成了i

对于i=ji=ji=j的情况,就得减去多算的pipi+1p_ip_{i+1}pipi+1;对于i=j+1,j=i+1i=j+1,j=i+1i=j+1,j=i+1的情况,得减去多算的pi+1p_{i+1}pi+1情况,我们就先把错误算的所有概率都减掉,再把这种情况时的正确概率加上
因为有两种,前面的系数就是2,也就转化成了
(∑i=1n−1pipi+1)2+2∑i=1n−2pipi+1pi+2−2∑i=1n−2pipi+12pi+2−∑i=1n−1pi2pi+12+∑i=1n−1pipi+1(\sum_{i=1}^{n-1}p_ip_{i+1})^2+2\sum_{i=1}^{n-2}p_ip_{i+1}p_{i+2}-2\sum_{i=1}^{n-2}p_ip_{i+1}^2p_{i+2}-\sum_{i=1}^{n-1}p_i^2p_{i+1}^2+\sum_{i=1}^{n-1}p_ip_{i+1}(i=1n1pipi+1)2+2i=1n2pipi+1pi+22i=1n2pipi+12pi+2i=1n1pi2pi+12+i=1n1pipi+1
在这里插入图片描述


+2∑2≤i<nxi+2\sum_{2≤i<n}x_i+22i<nxi
对于这种情况舒服吧,不用变通直接转移成
+2∑i=2n−1pi+2\sum_{i=2}^{n-1}p_i+2i=2n1pi


−2∑1≤i<nxixi+1-2\sum_{1≤i<n}x_ix_{i+1}21i<nxixi+1
对于这种情况也是不会有冲突的,可以直接转移
−2∑i=1n−1pipi+1-2\sum_{i=1}^{n-1}p_ip_{i+1}2i=1n1pipi+1
在这里插入图片描述


−2∑2≤i<n∑1≤j<nxixjxj+1-2\sum_{2≤i<n}\sum_{1≤j<n}x_ix_jx_{j+1}22i<n1j<nxixjxj+1
这里冲突的情况就是i=j,i=j+1i=j,i=j+1i=j,i=j+1,先减掉错误的概率统计,再加上正确的
由于这个式子前面的符号是−-,我们就变成加回统计错误的部分再减掉正确的
−2(∑i=2n−1pi)(∑i=1n−1pipi+1)−4∑i=1n−1pipi+1+2∑i=1n−1pi2pi+1+2∑i=1n−1pipi+12-2(\sum_{i=2}^{n-1}p_i)(\sum_{i=1}^{n-1}p_ip_{i+1})-4\sum_{i=1}^{n-1}p_ip_{i+1}+2\sum_{i=1}^{n-1}p_i^2p_{i+1}+2\sum_{i=1}^{n-1}p_ip_{i+1}^22(i=2n1pi)(i=1n1pipi+1)4i=1n1pipi+1+2i=1n1pi2pi+1+2i=1n1pipi+12
注意观察与下列式子区别在哪里??
−2(∑i=2n−1pi)(∑i=1n−1pipi+1)−4∑i=1n−1pipi+1+2∑i=2n−1pi2pi+1+2∑i=1n−2pipi+12-2(\sum_{i=2}^{n-1}p_i)(\sum_{i=1}^{n-1}p_ip_{i+1})-4\sum_{i=1}^{n-1}p_ip_{i+1}+2\sum_{i=2}^{n-1}p_i^2p_{i+1}+2\sum_{i=1}^{n-2}p_ip_{i+1}^22(i=2n1pi)(i=1n1pipi+1)4i=1n1pipi+1+2i=2n1pi2pi+1+2i=1n2pipi+12
在这里插入图片描述
区别1:
+2∑i=1n−1pi2pi+1,+2∑i=2n−1pi2pi+1+2\sum_{i=1}^{n-1}p_i^2p_{i+1},+2\sum_{i=2}^{n-1}p_i^2p_{i+1}+2i=1n1pi2pi+1,+2i=2n1pi2pi+1
发现少了一次i=1i=1i=1的值累加,那我们把它加回来
+2∑i=1n−1pi2pi+1=+2∑i=2n−1pi2pi+1+2p1p2+2\sum_{i=1}^{n-1}p_i^2p_{i+1}=+2\sum_{i=2}^{n-1}p_i^2p_{i+1}+2p_1p_2+2i=1n1pi2pi+1=+2i=2n1pi2pi+1+2p1p2
区别2:
+2∑i=1n−1pipi+12,+2∑i=1n−2pipi+12+2\sum_{i=1}^{n-1}p_ip_{i+1}^2,+2\sum_{i=1}^{n-2}p_ip_{i+1}^2+2i=1n1pipi+12,+2i=1n2pipi+12
发现少了一次i=n−1i=n-1i=n1的值累加,那我们也把它加回来
+2∑i=1n−1pipi+12=+2∑i=1n−2pipi+12+2pn−1pn+2\sum_{i=1}^{n-1}p_ip_{i+1}^2=+2\sum_{i=1}^{n-2}p_ip_{i+1}^2+2p_{n-1}p_n+2i=1n1pipi+12=+2i=1n2pipi+12+2pn1pn
最后我们把这种情况转移成
−2(∑i=2n−1pi)(∑i=1n−1pipi+1)−4∑i=1n−1pipi+1+2∑i=2n−1pi2pi+1+2∑i=1n−2pipi+12+2p1p2+2pn−1pn-2(\sum_{i=2}^{n-1}p_i)(\sum_{i=1}^{n-1}p_ip_{i+1})-4\sum_{i=1}^{n-1}p_ip_{i+1}+2\sum_{i=2}^{n-1}p_i^2p_{i+1}+2\sum_{i=1}^{n-2}p_ip_{i+1}^2+2p_1p_2+2p_{n-1}p_n2(i=2n1pi)(i=1n1pipi+1)4i=1n1pipi+1+2i=2n1pi2pi+1+2i=1n2pipi+12+2p1p2+2pn1pn


这也就是为什么我们用p代替x的时候公式里面出现了常数项的原因
最后把这拆开的六个式子合并同类项得到了上述公市

出于对代码更好操作的数据结构维护和便于合并,我们才把最后一种情况拆了一下
接下来就是如何维护八个不同的p求和,实话告诉你就是八棵线段树
但我选择了重载一次加号,一棵线段树维护八个不同的值。。。
在这里插入图片描述

代码实现

#include <cstdio>
#define mod 998244353
#define LL long long
#define MAXN 100005
struct node {LL p, p2, pp, p2p, pp2, p2p2, ppp, pp2p;node ( LL p = 0, LL p2 = 0, LL pp = 0, LL p2p = 0, LL pp2 = 0, LL p2p2 = 0, LL ppp = 0, LL pp2p = 0 ) :p ( p ), p2 ( p2 ), pp ( pp ), p2p ( p2p ), pp2 ( pp2 ), p2p2 ( p2p2 ), ppp ( ppp ), pp2p ( pp2p ) {} 
}tree[MAXN << 2];
int n, m;
LL p[MAXN];node operator + ( const node &u, node &v ) {return node ( ( u.p + v.p ) % mod, ( u.p2 + v.p2 ) % mod, ( u.pp + v.pp ) % mod, ( u.p2p + v.p2p ) % mod,( u.pp2 + v.pp2 ) % mod, ( u.p2p2 + v.p2p2 ) % mod,( u.ppp + v.ppp ) % mod, ( u.pp2p + v.pp2p ) % mod );
}void count ( int num, int i ) {tree[num].p = p[i] % mod;tree[num].p2 = p[i] * p[i] % mod;tree[num].pp = p[i] * p[i + 1] % mod;tree[num].p2p = tree[num].p2 * p[i + 1] % mod;tree[num].pp2 = tree[num].pp * p[i + 1] % mod;tree[num].ppp = tree[num].pp * p[i + 2] % mod;tree[num].pp2p = tree[num].pp2 * p[i + 2] % mod;tree[num].p2p2 = tree[num].pp * tree[num].pp % mod;
}void build ( int num, int l, int r ) {if ( l == r ) {count ( num, l );return;}int mid = ( l + r ) >> 1;build ( num << 1, l, mid );build ( num << 1 | 1, mid + 1, r );tree[num] = tree[num << 1] + tree[num << 1 | 1];
}void update ( int num, int l, int r, int id ) {if ( l == r ) {count ( num, id );return;}int mid = ( l + r ) >> 1;if ( id <= mid )update ( num << 1, l, mid, id );elseupdate ( num << 1 | 1, mid + 1, r, id );tree[num] = tree[num << 1] + tree[num << 1 | 1];
}node range_sum ( int num, int l, int r, int L, int R ) {if ( L <= l && r <= R )return tree[num];int mid = ( l + r ) >> 1;node lsum( 0, 0, 0, 0, 0, 0, 0, 0 ), rsum( 0, 0, 0, 0, 0, 0, 0, 0 );if ( L <= mid )lsum = range_sum ( num << 1, l, mid, L, R );if ( mid < R )rsum = range_sum ( num << 1 | 1, mid + 1, r, L, R );return lsum + rsum;
}LL query ( int l, int r ) {if ( l == r )return ( 1 - p[l] + mod ) % mod;node t = range_sum ( 1, 1, n, l, r );t.p = ( ( t.p - p[r] - p[l] ) % mod + mod ) % mod;t.p2 = ( ( t.p2 - p[r] * p[r] % mod - p[l] * p[l] % mod ) % mod + mod ) % mod;t.p2p = ( ( t.p2p - p[l] * p[l] % mod * p[l + 1] % mod- p[r] * p[r] % mod * p[r + 1] % mod ) % mod + mod ) % mod;t.pp2 = ( ( t.pp2 - p[r - 1] * p[r] % mod * p[r] % mod- p[r] * p[r + 1] % mod * p[r + 1] % mod ) % mod + mod ) % mod;t.pp = ( ( t.pp - p[r] * p[r + 1] % mod ) % mod + mod ) % mod;t.p2p2 = ( ( t.p2p2 - p[r] * p[r] % mod * p[r + 1] % mod * p[r + 1] % mod ) % mod + mod ) % mod;t.ppp = ( ( t.ppp - p[r - 1] * p[r] % mod * p[r + 1] % mod- p[r] * p[r + 1] % mod * p[r + 2] % mod ) % mod + mod ) % mod;t.pp2p = ( ( t.pp2p - p[r - 1] * p[r] % mod * p[r] % mod * p[r + 1] % mod- p[r] * p[r + 1] % mod * p[r + 1] % mod * p[r + 2] % mod ) % mod + mod ) % mod;LL ans = ( ( 1 + t.p * t.p % mod + 3 * t.p % mod - t.p2 + t.pp * t.pp % mod+ 2 * t.ppp % mod - 2 * t.pp2p % mod - 5 * t.pp % mod + 2 * t.p2p % mod + 2 * t.pp2 % mod - t.p2p2 - 2 * t.p * t.pp % mod+ 2 * p[l] * p[l + 1] % mod + 2 * p[r - 1] * p[r] % mod ) % mod + mod ) % mod;return ans;
}int main() {scanf ( "%d %d", &n, &m );for ( int i = 1;i <= n;i ++ ) {scanf ( "%lld", &p[i] );p[i] = ( mod - p[i] ) % mod; }build ( 1, 1, n );for ( int i = 1;i <= m;i ++ ) {int opt;scanf ( "%d", &opt ); if ( opt == 1 ) {int x, q;scanf ( "%d %d", &x, &q );p[x] = ( mod - q ) % mod;update( 1, 1, n, x );if ( x > 1 )update ( 1, 1, n, x - 1 );if ( x > 2 )update ( 1, 1, n, x - 2 );}else {int l, r;scanf ( "%d %d", &l, &r );printf ( "%lld\n", query ( l, r ) % mod );}} return 0;
}

如果对于推导过程有疑惑的,看不明白的,可以随时评论,也欢迎指出打错的部分,太多了,错打了几个字符很正常,谢谢
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318154.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P3992 [BJOI2017]开车

P3992 [BJOI2017]开车 题意&#xff1a; 题解&#xff1a; 我们要先将问题转换 圈是车&#xff0c;x是加油站。红色部分为车移动的路线 数组a是车数量的前缀和 数组b是加油站的前缀和 而a[i]与b[i]的差的绝对值就是对应的红色路被走的次数 现在车发生位置移动&#xff0c;b数…

IdentityServer4-MVC+Hybrid实现Claims授权验证(四)

上节IdentityServer4-客户端的授权模式原理分析&#xff08;三&#xff09;以对话形式&#xff0c;大概说了几种客户端授权模式的原理&#xff0c;这节重点介绍Hybrid模式在MVC下的使用。且为实现IdentityServer4从数据库获取User进行验证&#xff0c;并对Claim进行权限设置打下…

漫谈何时从单体架构迁移到微服务?

面对微服务如火如荼的发展&#xff0c;很多人都在了解&#xff0c;学习希望能在自己的项目中帮得上忙&#xff0c;当你对微服务的庐山真面目有所了解后&#xff0c;接下来就是说服自己了&#xff0c;到底如何评估微服务&#xff0c;什么时候使用微服务&#xff0c;什么时间点最…

[CSP-S Day1,Day2 游记]提高组考后总结及学习编程C++以来的心得体会

怀着沉重而感慨的心情写下了这篇blog考试中暴露的问题Day1Day2综上解决方法学习历程及以来的心得体会职业精神这篇博客我可能会写好几天&#xff0c;我jio得这篇博客对我的学习历程以及态度产生深刻影响考试中暴露的问题 首先先说这次提高组考试的每道题所遇到的各种问题吧 Da…

【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程...

上篇文章介绍了基于Ids4密码授权模式&#xff0c;从使用场景、原理分析、自定义帐户体系集成完整的介绍了密码授权模式的内容&#xff0c;并最后给出了三个思考问题&#xff0c;本篇就针对第一个思考问题详细的讲解下Ids4是如何生成access_token的&#xff0c;如何验证access_t…

P5049 [NOIP2018 提高组] 旅行

P5049 [NOIP2018 提高组] 旅行 题意&#xff1a; 一棵树(可能是基环树)&#xff0c;从1出发&#xff0c;每到达一个新的点就记录下编号。求一种走法使得记录下来的编号字典序最小。 1≤n≤500000 mn−1 或 mn 题解&#xff1a; 如果不是基环树&#xff0c;那直接每次走字典…

[2019CSP-S Day1]提高组Day1题解(格雷码[模拟(k转二进制取反的做法带证明)] + 括号树[DP] + 树上的数(暴力+菊花图+单链))

Day1T1&#xff1a;格雷码题目题解代码实现T2&#xff1a;括号树题目题解代码实现T3&#xff1a;树上的数题目10pts暴力题解代码实现25pts菊花图题解代码实现25pts单链题解代码实现T1&#xff1a;格雷码 题目 通常&#xff0c;人们习惯将所有 n位二进制串按照字典序排列&…

使用PerfView监测.NET程序性能(四):折叠,过滤和时间范围选择

在上一篇文章使用PerfView监测.NET程序性能&#xff08;三&#xff09;&#xff1a;分组中&#xff0c;我们使用了Perfview的分组功能。分组功能旨在对某些函数按照某个格式进行分组&#xff0c;以减少视图中的各种无关函数的数量。但仅有分组还不够&#xff0c;有时我们想将一…

带旋treap概念及模板,带例题:普通平衡树

带旋Treap二叉查找树BST(Binary Search Tree)定义Treap定义模板合集&#xff08;均为O(logn)O(logn)O(logn)&#xff09;push_up模板旋转模板插入模板删除模板查找前驱模板查找后驱模板查找键值key模板查找节点的修正值rank模板PS&#xff1a;rd的比较问题例题&#xff1a;普通…

微服务系列实践 .NET CORE

从事这个行业转眼已经6年了&#xff0c;从当初刚毕业的在北京朝八晚十&#xff0c;从二环到五环&#xff0c;仍每天精力充沛的小愤青&#xff1b;再到深圳一点一滴的辛勤在软件行业的耕种&#xff0c;从当初单体应用架构到现在微服务架构的经历&#xff0c;回想起来自己的收获倒…

P2607 [ZJOI2008]骑士

P2607 [ZJOI2008]骑士 题意&#xff1a; n个点n个边&#xff0c;每个点都有权值&#xff0c;相邻的点不能同时选择&#xff0c;问如何选择能使得权值最大 题解&#xff1a; 这个题很有P1352 没有上司的舞会这个题的感觉&#xff0c;唯一的区别是那个题保证是树&#xff0c;…

模板:线段树优化建图

前言 百川到海&#xff0c;天下归一 解析 线段树优化建图是用于对一个区间的点连边时的优化方法 建一棵in树一棵出树分别往上和下指即可 大概长这样 &#xff08;pia的洛谷的照片&#xff09; 建树 正常动态开点即可 void build(int &k,int l,int r){tr[ktot](tree){0…

[非旋平衡树]fhq_treap概念及模板,例题:普通平衡树,文艺线段树

文章目录概念全套模板push_up模板split拆树模板(按权值拆)split拆树模板(按个数拆)merge合并模板&#xff08;地址版&#xff09;merge合并模板&#xff08;带返回根&#xff09;区间模板insert插入模板delete删除模板find_kth找第k大模板get_rank找排名模板pre找前驱模板suf找…

surging 微服务引擎 1.0 正式发布

surging 是一个分布式微服务引擎,提供高性能RPC远程服务调用&#xff0c;服务引擎支持http、TCP、WS、Mqtt协议,采用Zookeeper、Consul作为surging服务的注册中心&#xff0c;集成了哈希一致性&#xff0c;随机&#xff0c;轮询、压力最小优先作为负载均衡的算法&#xff0c;底…

YBTOJ:彩色圆环

文章目录前言题目描述InputOutputSample InputSample Output解析代码前言 尽信书&#xff0c;则不如无书 题目描述 Input 仅有一行&#xff0c;该行给出依次两个正整数N, M&#xff0c;分别表示宝石的个数和宝石在变化时可能变成的颜色种类数。 Output 应仅有一行&#xff0…

【2019CSP-J 普及组题解】数字游戏(number),公交换乘(transfer),纪念品(souvenir),加工领奖(work) CSP普及游记

文章目录T1&#xff1a;数字游戏题目CODET2&#xff1a;公交换乘题目CODET3&#xff1a;纪念品题目题解CODET4&#xff1a;加工领奖题目题解CODE关于普及组的想法&游记T1&#xff1a;数字游戏 题目 小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串来玩数字游戏&…

搭建基于Docker社区版的Kubernetes本地集群

Kubernetes的本地集群搭建是一件颇费苦心的活&#xff0c;网上有各种参考资源&#xff0c;由于版本和容器的不断发展&#xff0c;搭建的方式也是各不相同&#xff0c;这里基于Docker CE的18.09.0版本&#xff0c;在Mac OS、Win10下分别搭建了一次。一、Mac OS下搭建安装Docker …

Infinite Tree

Infinite Tree 题意&#xff1a; 题解&#xff1a; 参考博客 看了好一阵子才明白。。。emm。 我们先按照题意画出一部分树 我们先不考虑复杂度&#xff0c;这题应该怎么做&#xff1f; 题目给了每个点的权值w[i]&#xff0c;问一个点到所有的节点路径长度*点权之和最小是多少…

IdentityServer4-从数据库获取User登录并对Claims授权验证(五)

本节将在第四节基础上介绍如何实现IdentityServer4从数据库获取User进行验证&#xff0c;并对Claim进行权限设置。一、新建Web API资源服务&#xff0c;命名为ResourceAPI&#xff08;1&#xff09;新建API项目&#xff0c;用来进行user的身份验证服务。&#xff08;2&#xff…

周末狂欢赛1(玩游戏/Game,函数,JOIOI王国)

狂欢1T1&#xff1a;玩游戏 / Game题目题解代码实现T2&#xff1a;函数题目题解代码实现T3&#xff1a;JOIOI王国题目题解代码实现T1&#xff1a;玩游戏 / Game 题目 ljcc 和他的学妹在玩游戏&#xff0c;这个游戏共有 n 轮&#xff0c;在第 i 轮获胜会获得 i 分&#xff0c;…