高一上十月中旬日记
10.11
闲话
- @Estelle_N , @5k_sync_closer , @int_R , @K8He 搬来我们机房了。
- 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。
- 下午到机房后放 @xyin 的每日一歌《沉溺》,然后听多校的讲题。讲完题后被 \(huge\) \(D\) 了下昨天晚上讲题后半段太乱了,让我们(尤指高一的)不要聚堆聊天。
- 详见 2024 CSP-S 游记 10.11 。
- 开完动员会就让我们回来了。回来后 \(miaomiao\) 提议让我们也跟着下去活动活动,在得知我们一周只有一节体育课且还在周三上午(另外一节被艺术课占了)后说那就周六体活时间增长至 \(17:00 \sim 19:00\) ,让我们活动活动。在 \(miaomiao\) 催 \(CSP-S\) 二轮的缴费后,又说自初三下学期开始他没有严格要求我们模拟赛改题和专题进度的问题,让我们自己合理规划,而隔壁 \(huge\) 在召开“小班会”。
做题纪要
CF573B Bear and Blocks
P359. 好数(number)
P360. SOS字符串(sos)
P361. 集训营的气球(balloon)
P362. 连通子树与树的重心(tree)
10.12
闲话
- 侯操时得知了明天下午第八、九节课是高校校园行活动,晚上我们 \(18:00\) 吃饭, \(18:25\) 在操场指定位置集合进行给来做报告的老师进行团体操表演。
- 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。
- 下午放 @lxyt_415x 的每日一歌《动物世界》,然后听多校的讲题。讲题的时候 \(huge\) 坐在前面盯着我们, \(feifei\) 坐在后面盯着我们。讲完题后 \(huge\) 让我们找隔壁班问问我们什么时候上体育课,貌似几个教练之间的沟通不太具有时效性。
- 体活 \(miaomiao\) 本来是让我们 \(17:00\) 开始上的,但因为其他高一的 \(17:25\) 才开始上体活,遂 \(feifei,field\) 找 \(miaomiao\) 问我们什么时候开始上,得到的回复是让 \(17:25\) 开始上。体活开始后先去了趟书店买书,回来时在机房楼下碰见 @APJifengc 和 @Estelle_N 在聊天,简单唠了几句关于运动会的事情,然后就上楼把 @int_R 拉下来一起聊天了。吃完晚饭众人又跟着 @APJifengc 在校园里乱逛、聊天。
- 晚上回宿舍后得知我们不用考一调了,赢麻了。
做题纪要
牛客 NC277168 连分数
-
\(f(n)\) 较 \(f(n-1)\) 来说在分母处多了个 \(\frac{1}{a}\) 。在 \(x\) 趋近于无穷的情况下,这个影响可以忽略不计,故有 \(x=a+\frac{1}{x}\) ,解得 \(x=\frac{a \pm \sqrt{a^{2}+4}}{2}\) 。
点击查看代码
int main() { int t,i; double a; cin>>t; for(i=1;i<=t;i++) { cin>>a; printf("%.12lf\n",(a+sqrt(a*a+4))/2); } return 0; }
P363. 小 Z 的手套(gloves)
luogu P7230 [COCI2015-2016#3] NEKAMELEONI
P364. 小 Z 的字符串(string)
[ABC375A] Seats
[ABC375B] Traveling Takahashi Problem
[ABC375D] ABA
[ABC375E] 3 Team Division
10.13
闲话
- 因为是考试周,所以早上没有体活,要求 \(6:20\) 到班准备公共早读,起床后遂直接去机房了。隔壁高二的早上有体活,所以没有教练来盯班。
- 上午 \(feifei\) 说今天多校的安排是写 数学1(容斥、组合数学、期望) ,明天打完学校模拟赛后会在晚上进行讲题。
- 下午到机房后 \(4\) 台教师机仅有 \(miaomiao\) 的开着显示屏,误认为剩下的都关机了,而且 \(feifei\) 到位到的比我都晚,因“社恐”没去找 \(feifei\) 放我的每日一歌《回到奥奇》。
- 高校校园行安排在了第八、九节课, \(feifei\) 说让我们自愿参加,且仅能在第八节课去听,但估计他自己都不知道第八、九节课什么时候上/下课,毕竟 \(15:35\) 校园里人就开始四处乱跑了,他还让我们 \(15:45\) 才下去。跟着 @K8He 和 @jijidawang 和 @5k_sync_closer 去西扩听复旦的宣讲去了,在教室里遇见了 @Flandres 。到教室旁的时候里面人已经很多了,但后门还有点位置,遂挤了进去,然后发现位置太靠后了且前面的人有点高,过了一会儿发现 @DanhengYinyue 和 @AqrDAD 从前门挤了进来。做宣讲的老师讲了下复旦校名由来,部分校史,国家给予的政策和任务,学生培养理念(以“自由”为主,包括但不限于医学系和非医学系在内的自由转专业),优秀的师资、场馆、伙食和丰富的课外生活,地理位置优越(距离多家网络公司本部较近,方便学生实习),部分高精尖专业给予的政策和条件。因时间有点紧张遂招生政策只是略微提了一嘴,纯 \(whk\) 得 \(670+\) ,结束后得知通过 @DanhengYinyue 和 @AqrDAD 与他的交谈感觉不太像招生办的老师,至少不是计算机专业。
- 临吃晚饭的时候 \(feifei\) 重复了下昨天早操时的通知,让我们 \(18:00\) 去吃饭,需要跳团体操的同学 \(18:25\) 到操作指定地点跳操然后回教室收拾考场,其他人直接回教室收拾考场。吃完晚饭往操场走的路上发现他们 \(18:00\) 左右已经开始跳了,我们到的时候基本已经跳完了,遂直接回教室收拾了趟书。我的桌子上有一摊不知道谁的资料且成分复杂,而且近几天发的学案、自助、作业啥都没有。不想考完一调再回来收拾考场故把除杂课、政史地外的书搬到机房了。
- 晚上 \(feifei\) 往 \(ftp\) 里放了几篇读物,懒得挂链接了。
做题纪要
[ABC375G] Road Blocked 2
[ABC375F] Road Blocked
[ABC375C] Spiral Rotation
CF665E Beautiful Subarrays
-
考虑固定右端点 \(r\) ,统计 \(l \in [1,n]\) 中满足 \(sum_{r} \bigoplus sum_{l-1} \ge k\) 的数量。
-
设当前二进制表示下 \(k\) 的第 \(i\) 位为 \(k_{i}\) , \(sum_{r}\) 的第 \(i\) 位为 \(sum_{r,i}\) 。
-
若 \(k_{i}=1\) 则必须走向 \(sum_{r,i} \bigoplus 1\) 方向的子树。否则, \(sum_{r,i} \bigoplus 1\) 方向的子树内的所有节点都合法可以直接加上,另一方向上的子树内的节点大小关系不确定需要继续往下走。
-
当走到叶子节点时需要加上异或和 \(=k\) 的情况。
点击查看代码
struct Trie { int son[32000010][2],cnt[32000010],rt_sum; void insert(int s) { int x=0; for(int i=30;i>=0;i--) { if(son[x][(s>>i)&1]==0) { rt_sum++; son[x][(s>>i)&1]=rt_sum; } x=son[x][(s>>i)&1]; cnt[x]++; } } ll query(int s,int k) { ll ans=0; int x=0; for(int i=30;i>=0;i--) { if((k>>i)&1) { x=son[x][((s>>i)&1)^1]; } else { ans+=cnt[son[x][((s>>i)&1)^1]]; x=son[x][(s>>i)&1]; } if(x==0) { break; } } return ans+cnt[x];//加上等于 k 的贡献 } }T; int main() { int n,k,sum=0,x,i; ll ans=0; cin>>n>>k; T.insert(0); for(i=1;i<=n;i++) { cin>>x; sum^=x; ans+=T.query(sum,k); T.insert(sum); } cout<<ans<<endl; return 0; }
P366. 异或区间(xor)
luogu P4755 Beautiful Pair
-
将 \(a_{l}a_{r} \le \max\limits_{i=l}^{r} \{ a_{i} \}\) 转化为 \(a_{l} \le \frac{\max\limits_{i=l}^{r} \{ a_{i} \}}{a_{r}}\) 。
-
后面的做法基本同 P366. 异或区间(xor) ,原可持久化 \(01Trie\) 换成主席树即可。
点击查看代码
int a[100010],l[100010],r[100010]; stack<int>s; struct PDS_SMT { int root[100010],rt_sum=0; struct SegmentTree { int ls,rs,sum; }tree[100010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int pre,int &rt,int l,int r,int pos) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum++; if(l==r) { return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos); } else { update(rson(pre),rson(rt),mid+1,r,pos); } } int query(int rt1,int rt2,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt2].sum-tree[rt1].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt1),lson(rt2),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y); } return ans; } }T; int main() { int n,pos,i,j; ll ans=0; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; T.update(T.root[i-1],T.root[i],1,1000000000,a[i]); while(s.empty()==0&&a[s.top()]<a[i]) { s.pop(); } l[i]=(s.empty()==0)?s.top()+1:1; s.push(i); } while(s.empty()==0) { s.pop(); } for(i=n;i>=1;i--) { while(s.empty()==0&&a[s.top()]<=a[i]) { s.pop(); } r[i]=(s.empty()==0)?s.top()-1:n; s.push(i); } for(i=1;i<=n;i++) { if(i-l[i]+1<r[i]-i+1) { for(j=l[i];j<=i;j++) { ans+=T.query(T.root[i-1],T.root[r[i]],1,1000000000,1,a[i]/a[j]); } } else { for(j=i;j<=r[i];j++) { ans+=T.query(T.root[l[i]-1],T.root[i],1,1000000000,1,a[i]/a[j]); } } } cout<<ans<<endl; return 0; }
luogu P11188 「KDOI-10」商店砍价
-
手摸一下发现至多一次性删 \(6\) 个数。
-
将操作 \(2\) 按位拆贡献后做 \(01\) 背包 \(DP\) 即可。
-
正着处理比反着处理多带一个 \(6\) 的常数。
点击查看代码
ll v[20],cnt[20],f[2][15],pw[15]; char s[100010]; int main() { ll c,t,n,ans,i,j,k,h; scanf("%lld%lld",&c,&t); for(i=0;i<=8;i++) { pw[i]=(i==0)?1:pw[i-1]*10; } for(h=1;h<=t;h++) { ans=0x7f7f7f7f7f7f7f7f; scanf("%s",s+1); n=strlen(s+1); for(i=1;i<=9;i++) { cnt[i]=0; scanf("%lld",&v[i]); } for(i=1;i<=n;i++) { cnt[s[i]-'0']++; } for(k=0;k<=min(n,8ll);k++) { memset(f,0x3f,sizeof(f)); f[0][0]=0; for(i=1;i<=n;i++) { for(j=0;j<=min(i,k);j++) { f[i&1][j]=f[(i-1)&1][j]+v[s[i]-'0']; if(j-1>=0) { f[i&1][j]=min(f[i&1][j],f[(i-1)&1][j-1]+(s[i]-'0')*pw[k-j]); } } } ans=min(ans,f[n&1][k]); } printf("%lld\n",ans); } return 0; }
CF932E Team Work
10.14
闲话
- 侯操时德育主任说了下今天考试的时间安排,中午延迟到了 \(12:05\) 吃饭。因为即将起雾,所以跑操取消了,让直接去教室。
- 到机房后发现隔壁在上早读。过了一会儿 \(huge\) 进来了问我们怎么现在就来机房了,我们说 \(miaomiao\) 让跑完操就来机房。
- 上午 \(7:30 \sim 11:30\) 打学校 \(OJ\) 的模拟赛。
- 午饭到 \(12:00\) 就直接走了,到食堂发现高二的提前下来吃饭了。
- 下午放 @oceans_of_stars 的每人一歌《我们会像风》。开始讲题的时候已经快 \(16:00\) 了。
- 打晚饭铃时我们正打算跟着高二的一起去吃饭, \(huge\) 问我们高一正常几点吃饭,我们说 \(18:28\) 去吃饭,然后 \(huge\) 就让我们也跟着去吃饭了。
- 晚上 \(huge\) 盯班时强调了下讨论时间和做题时间的界限,声称要打印张时间表贴在墙上但没有落实;多校没有组织讲题,可能是因为写题的人太少了(?)
做题纪要
luogu P3648 [APIO2014] 序列分割
-
在切割方案确定的情况,切割顺序无关。
-
令 \(sum_{i}=\sum\limits_{j=1}^{i}a_{j}\) 。
-
设 \(f_{i,j}\) 表示前 \(i\) 个数中切割成了 \(j\) 个块的最大得分,状态转移方程为 \(f_{i,j}=\max\limits_{k=0}^{i-1} \{ f_{k,j-1}+sum_{k}(sum_{i}-sum_{k}) \}\) ,边界为 \(f_{0,0}=0\) 。
- 对于某个块内的单个元素恰好只会与其他块内的任何一个元素恰好乘一次。
-
去掉 \(\max\) ,有 \(\begin{cases} x=sum_{k} \\ y=f_{k,j-1}-sum_{k}^{2} \\ k=-sum_{i} \\ b=f_{i,j} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 都是单调递增的。
-
交换 \(i,j\) 两维后进行斜率优化 \(DP\) 即可。
点击查看代码
ll a[100010],sum[100010],f[2][100010]; int opt[210][100010]; deque<ll>q; ll x(ll i) { return sum[i]; } ll y(ll i,ll j) { return f[j&1][i]-sum[i]*sum[i]; } void print(int x,int k) { if(k==0) { return; } print(opt[k][x],k-1); cout<<opt[k][x]<<" "; } int main() { ll n,k,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]; sum[i]=sum[i-1]+a[i]; } for(j=1;j<=k;j++) { q.clear(); q.push_back(0); f[j&1][0]=0; for(i=1;i<=n;i++) { while(q.size()>=2&&(y(q[1],j-1)-y(q.front(),j-1))>=-sum[i]*(x(q[1])-x(q.front()))) { q.pop_front(); } opt[j][i]=q.front(); f[j&1][i]=f[(j-1)&1][q.front()]+sum[q.front()]*(sum[i]-sum[q.front()]); while(q.size()>=2&&(y(q.back(),j-1)-y(q[q.size()-2],j-1))*(x(i)-x(q.back()))<=(y(i,j-1)-y(q.back(),j-1))*(x(q.back())-x(q[q.size()-2]))) { q.pop_back(); } q.push_back(i); } } cout<<f[k&1][n]<<endl; print(n,k); return 0; }
T2203. 玩水 (water)
T2212. AVL 树
QOJ 1251. Even rain
10.15
闲话
- 因 \(7:00\) 去吃饭,所以在 \(7:15\) 左右就回到了机房,然后发现下载下发文件需要下 \(5 \min\) 。然后 \(miaomiao\) 进来了说我们等 \(7:30\) 再开始打,学校 \(OJ\) 的下载速度还快点。
- 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。
- 下午放 @hypixel_zhb666 的每日一歌《王妃》;然后听多校的讲题,讲完题 \(huge\) 就让隔壁高二的去上体育课了。
- 下午 \(huge\) 说让我们明天做 数学1(容斥、组合数学、期望) 和 搜索、模拟 。
- 等高一正常考完一调后, \(miaomiao\) 跟我们说让回班收拾下考场,另外本周五、六我们教室被“征用”作为成人高考的考场了,所以不用收拾太多东西。回班后找寻了下自己的桌子但无果,椅子还是原来的的椅子,位置没变,但同桌变成了 @wang54321 。
- 晚上有虫子在机房乱飞, \(miaomiao\) 让我们别管它,然后就掉在了 \(miaomiao\) 机箱后面。
做题纪要
luogu P5337 [TJOI2019] 甲苯先生的字符串
-
多倍经验: CF222E Decoding Genome
-
设 \(f_{i,j}\) 表示处理到第 \(i\) 位时,第 \(i\) 位为字符 \(j\) 的方案数,状态转移方程为 \(f_{i,j}=\sum\limits_{k=1}^{26}[j,k 不相邻] \times f_{i-1,k}\) 。
-
矩阵优化 \(DP\) 即可。
点击查看代码
const ll p=1000000007; char s[100010]; struct Matrix { ll ma[30][30]; Matrix() { memset(ma,0,sizeof(ma)); } }f,a; Matrix mul(Matrix a,Matrix b,ll n,ll m,ll k,ll p) { Matrix c; for(ll i=1;i<=n;i++) { for(ll j=1;j<=k;j++) { for(ll h=1;h<=m;h++) { c.ma[i][j]=(c.ma[i][j]+a.ma[i][h]*b.ma[h][j]%p)%p; } } } return c; } Matrix qpow(Matrix a,ll b,ll p,ll n) { Matrix ans; for(ll i=1;i<=n;i++) { ans.ma[i][i]=1; } while(b) { if(b&1) { ans=mul(ans,a,n,n,n,p); } b>>=1; a=mul(a,a,n,n,n,p); } return ans; } int main() { ll b,len,n=1,m=26,k=26,ans=0,i,j; cin>>b>>(s+1); len=strlen(s+1); for(i=1;i<=26;i++) { f.ma[1][i]=1; for(j=1;j<=26;j++) { a.ma[i][j]=1; } } for(i=1;i<=len-1;i++) { a.ma[s[i]-'a'+1][s[i+1]-'a'+1]=0; } f=mul(f,qpow(a,b-1,p,m),n,m,k,p); for(i=1;i<=26;i++) { ans=(ans+f.ma[1][i])%p; } cout<<ans<<endl; return 0; }
P374. 限速(speed)
P376. 距离(distance)
P375. 酒鬼 (drunkard)
10.16
闲话
- 早操又被加圈了。
- 跑完操到机房后 \(field\) 问我们是哪科早读,说我们整天都待在机房不上早读会不会不好,但并没有做出一些实质性措施(隔壁已经被要求上早读了)。
- 下午放 @xrlong 的每日一歌《恋のうた(恋之歌)》。
- 晚上听多校的讲 数学1(容斥、组合数学、期望) 。
- 回宿舍后从同学的口中得知了班级各科平均分(政史地没考,除外)均是全年级第一(应该是三个校区 \(5\) 个级部)。
做题纪要
luogu P11184 带余除法
-
容易有 \(\begin{cases} kq \le kq+r=n \\ (k+1)q>n \end{cases}\) 得到 \(\left\lfloor \frac{n}{k+1} \right\rfloor < q=\frac{n-r}{k} \le \left\lfloor \frac{n}{k} \right\rfloor\) 。又因为 \(q\) 与 \(r\) 一一对应,所以输出 \(\left\lfloor \frac{n}{k} \right\rfloor-\left\lfloor \frac{n}{k+1} \right\rfloor\) 即可。
-
特判 \(k=0\) 时有 \(r=n\) ,故输出 \(1\) 。
点击查看代码
int main() { ll t,n,k,i; cin>>t; for(i=1;i<=t;i++) { cin>>n>>k; if(k==0) { cout<<1<<endl; } else { cout<<n/k-n/(k+1)<<endl; } } return 0; }
luogu P11187 配对序列
-
设 \(f_{i,0/1}\) 表示以 \(a_{i}\) 结尾且 \(a_{i}\) 出现在偶数/奇数个位置上的子序列最大长度。状态转移方程为 \(\begin{cases} f_{i,0}=\max\limits_{j=1}^{i-1}\{ [a_{j}=a_{i}] \times (f_{j,1}+1) \} \\ f_{i,1}=\max\limits_{j=1}^{i-1}\{ [a_{j} \ne a_{i}] \times (f_{j,0}+1) \}\end{cases}\) 。
-
对着值域冲线段树优化 \(DP\) 即可。
-
最终有 \(\max\limits_{i=1}^{n}\{ f_{i,0} \}\) 即为所求。
点击查看代码
int a[500010],f[500010][2],g[500010]; struct SMT { struct SegmentTree { int maxx; }tree[2000010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void build(int rt,int l,int r) { if(l==r) { tree[r].maxx=0; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].maxx=max(tree[rt].maxx,val); return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(rt),l,mid,pos,val); } else { update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt].maxx; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans=max(ans,query(lson(rt),l,mid,x,y)); } if(y>mid) { ans=max(ans,query(rson(rt),mid+1,r,x,y)); } return ans; } }T; int main() { int n,ans=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } memset(g,-0x3f,sizeof(g)); T.build(1,1,500000); for(i=1;i<=n;i++) { f[i][0]=max(f[i][0],g[a[i]]+1); if(a[i]-1>=1) { f[i][1]=max(f[i][1],T.query(1,1,500000,1,a[i]-1)+1); } if(a[i]+1<=500000) { f[i][1]=max(f[i][1],T.query(1,1,500000,a[i]+1,500000)+1); } g[a[i]]=max(g[a[i]],f[i][1]); T.update(1,1,500000,a[i],f[i][0]); } for(i=1;i<=n;i++) { ans=max(ans,f[i][0]); } cout<<ans<<endl; return 0; }
T2198. 传统题
luogu P3794 签到题IV
-
\(\gcd\) 和 \(\operatorname{or}\) 在固定左端点的情况下至多会变化 \(O(\log V)\) 次。
-
以 \(\gcd\) 为例,考虑求出所有的四元组 \((l,r,x,val)\) 表示 \(\forall i \in [l,r],\gcd\limits_{j=i}^{x} \{ a_{j} \}=val\) 。
- 本题中因为 \(x\) 一维可以“滚”掉,所以省去不写。
-
具体地,枚举右端点 \(x\) ,类似单调栈的写法(本身是单调的),继承 \(x-1\) 的四元组并及时去重/重构栈。
-
处理完后判断一下每个值区间是否有交求贡献即可。
点击查看代码
struct node { ll val,l,r; }g[500010],o[500010]; ll a[500010],l[500010],r[500010],cnt_g,cnt_o; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll n,k,ans=0,len,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=1;i<=n;i++) { for(j=1;j<=cnt_g;j++) { g[j].val=gcd(g[j].val,a[i]); } cnt_g++; g[cnt_g]=(node){a[i],i,i}; len=0; for(j=1;j<=cnt_g;j++) { if(g[j].val==g[j-1].val) { g[len].r=g[j].r; } else { len++; g[len]=g[j]; } } cnt_g=len; for(j=1;j<=cnt_g;j++) { l[g[j].val]=g[j].l; r[g[j].val]=g[j].r; } for(j=1;j<=cnt_o;j++) { o[j].val|=a[i]; } cnt_o++; o[cnt_o]=(node){a[i],i,i}; len=0; for(j=1;j<=cnt_o;j++) { if(o[j].val==o[j-1].val) { o[len].r=o[j].r; } else { len++; o[len]=o[j]; } } cnt_o=len; for(j=1;j<=cnt_o;j++) { if(l[o[j].val^k]!=0&&min(o[j].r,r[o[j].val^k])>=max(o[j].l,l[o[j].val^k])) { ans+=min(o[j].r,r[o[j].val^k])-max(o[j].l,l[o[j].val^k])+1; } } for(j=1;j<=cnt_g;j++) { l[g[j].val]=r[g[j].val]=0; } } cout<<ans<<endl; return 0; }
luogu P5502 [JSOI2015] 最大公约数
-
多倍经验: luogu P7009 [CERC2013] Magical GCD | UVA1642 魔法GCD Magical GCD
-
同上题,取左端点更新即可。
点击查看代码
struct node { ll val,l; }g[100010]; ll a[100010],cnt_g; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll n,ans=0,len=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=1;i<=n;i++) { for(j=1;j<=cnt_g;j++) { g[j].val=gcd(g[j].val,a[i]); } cnt_g++; g[cnt_g]=(node){a[i],i}; len=0; for(j=1;j<=cnt_g;j++) { if(g[j].val==g[j-1].val) { continue; } else { len++; g[len]=g[j]; ans=max(ans,(i-g[j].l+1)*g[j].val); } } cnt_g=len; } cout<<ans<<endl; return 0; }
CF475D CGCDSSQ
-
用
map
存下答案和上题一样做即可。点击查看代码
struct node { ll val,l,r; }g[100010]; ll a[100010],cnt_g; map<ll,ll>ans; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll n,m,x,len=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=1;i<=n;i++) { for(j=1;j<=cnt_g;j++) { g[j].val=gcd(g[j].val,a[i]); } cnt_g++; g[cnt_g]=(node){a[i],i,i}; len=0; for(j=1;j<=cnt_g;j++) { if(g[j].val==g[j-1].val) { g[len].r=g[j].r; } else { len++; g[len]=g[j]; } } cnt_g=len; for(j=1;j<=cnt_g;j++) { ans[g[j].val]+=g[j].r-g[j].l+1; } } cin>>m; for(i=1;i<=m;i++) { cin>>x; cout<<ans[x]<<endl; } return 0; }
集美大学第十一届校程序设计竞赛(同步赛) H 讨论组
-
顺序结构。
点击查看代码
int main() { int n; cin>>n; cout<<n/3<<endl; return 0; }
集美大学第十一届校程序设计竞赛(同步赛) G 三点共线
-
循环结构。
点击查看代码
int x[110],y[110]; int main() { int n,flag=0,i,j,k; cin>>n; for(i=1;i<=n;i++) { cin>>x[i]>>y[i]; } for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { for(k=j+1;k<=n;k++) { flag|=((y[i]-y[j])*(x[k]-x[j])==(y[k]-y[j])*(x[i]-x[j])); } } } if(flag==1) { cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } return 0; }
集美大学第十一届校程序设计竞赛(同步赛) C 因子数小于等于4的个数
-
线性筛筛出 \(d(n)\) 即可。
点击查看代码
int prime[1000010],vis[1000010],d[1000010],low[1000010],sum[1000010],len=0; void isprime(int n) { memset(vis,0,sizeof(vis)); d[1]=1; for(int i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; d[i]=2; low[i]=i; } for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { low[i*prime[j]]=low[i]*prime[j]; if(i==low[i]) { d[i*prime[j]]=d[i]+1; } else { d[i*prime[j]]=d[i/low[i]]*d[low[i*prime[j]]]; } break; } else { low[i*prime[j]]=prime[j]; d[i*prime[j]]=d[i]*d[prime[j]]; } } } for(int i=1;i<=n;i++) { sum[i]=sum[i-1]+(d[i]<=4); } } int main() { int t,i,l,r; cin>>t; isprime(1000000); for(i=1;i<=t;i++) { cin>>l>>r; cout<<sum[r]-sum[l-1]<<endl; } return 0; }
集美大学第十一届校程序设计竞赛(同步赛) I 市场
-
模拟。
点击查看代码
ll a[100010]; int main() { ll n,sum=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; sum+=abs(a[i]-a[i-1]); } sum+=abs(a[n]-0); for(i=1;i<=n;i++) { cout<<sum-abs(a[i]-a[i-1])-abs(a[i+1]-a[i])+abs(a[i-1]-a[i+1])<<" "; } return 0; }
luogu P7914 [CSP-S 2021] 括号序列
CF908D New Year and Arbitrary Arrangement
10.17
闲话
- 跑完操到机房后发现隔壁在上早读,进屋的时候 \(huge\) 也跟着进来了,问我们 \(miaomiao\) 没说让我们回班上早读吗,在得到“没说”的答复和看到机房后面的一排桌子上全是 \(whk\) 资料后就走了。被 \(huge\) \(D\) 称每天来了第一件事就是逛论坛、刷帖子。
- 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。临开始时 \(huge\) 说 \(7:30 \sim 8:30\) 不允许上厕所,让我们想去或将要去赶紧去。
- 下午放 @xyin 的每日一歌《麦恩莉》,然后听多校的讲题。
- \(huge\) 把机房原上课铃改成了类似敲钟的铃声(同时在我们机房落实了“铃声制度”),在 \(feifei\) 得知后又换回了原铃声。
- 临吃晚饭时下雨了,去吃晚饭时雨基本不下了,吃完饭回机房的路上又开始下了。回到机房后 \(feifei\) 又跟我们说了下“铃声制度”。
- 晚上 \(feifei\) 跟我们说明天早读来机房上,后天再回班上早读,而且明天没有早读任务,让我们自己查漏补缺。但他没看见机房一排的 \(whk\) 资料吗,但一摊书还要我们再搬回去吗?而且后天因为成人高考我们在不在教室上早读还不一定呢。故我们对此异议很多,然后 \(feifei\) 说我们要是有异议就和 \(miaomiao\) 说,他不管,但这一听就像是 \(feifei\) 或 \(huge\) 的主意。
做题纪要
CF1139D Steps to One
P378. 传送 (teleport)
- 弱化版: [ABC065D] Built? | luogu P8074 [COCI2009-2010#7] SVEMIR | “迎新春,过大年”多校程序设计竞赛 H 二次元世界之寻找珂朵莉
- 详见 多校A层冲刺NOIP2024模拟赛08 T1 A. 传送 (teleport) 。
P380. 战场模拟器 (simulator)
P379. 排列 (permutation)
[ARC185A] mod M Game 2
-
观察到 \(n<m\) ,故当手里有至少两张牌时,不会立即输掉游戏。
-
当最后一局开始时,
Alice
打完了手中的所有牌,而Bob
手中仍有一张牌 \(x \in [1,n]\) 。 -
此时
Bob
必胜当且仅当 \((2\sum\limits_{i=1}^{n}i-x) \equiv 0 \pmod{m}\) ,即 \((2\sum\limits_{i=1}^{n}i) \bmod m \in [1,n]\) 。点击查看代码
int main() { ll t,n,m,i; cin>>t; for(i=1;i<=t;i++) { cin>>n>>m; if(1<=n*(n+1)%m&&n*(n+1)%m<=n) { cout<<"Bob"<<endl; } else { cout<<"Alice"<<endl; } } return 0; }
[ARC185B] +1 and -1
-
观察到 \(sum=\sum\limits_{i=1}^{n}a_{i}\) 全过程中保持不变。
-
操作 \(a_{i}\) 时越让 \(a_{i}\) 接近 \(a_{i+1}\) 时一定更优,不妨构造 \(\begin{cases} \forall i \in [1,n-sum \bmod n],b_{i}=\left\lfloor \frac{sum}{n} \right\rfloor \\ \forall i \in (n-sum \bmod n,n],b_{i}=\left\lfloor \frac{sum}{n} \right\rfloor+1 \end{cases}\) 。
-
为保证对于任意前缀都有 \(-1\) 的数量不多于 \(+1\) 的数量,故当 \(\min\limits_{i=1}^{n}\{ \sum\limits_{j=1}^{i}b_{j}-a_{j} \} \ge 0\) 时合法。
点击查看代码
ll a[200010],b[200010]; int main() { ll t,n,sum,minn,i,j; cin>>t; for(j=1;j<=t;j++) { cin>>n; sum=0; minn=0x7f7f7f7f; for(i=1;i<=n;i++) { cin>>a[i]; sum+=a[i]; } for(i=1;i<=n;i++) { b[i]=sum/n+(n-sum%n<i); } sum=0; for(i=1;i<=n;i++) { sum+=b[i]-a[i]; minn=min(minn,sum); } if(minn>=0) { cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } } return 0; }
CF341D Iahub and Xors
-
二维树状数组维护子矩阵异或,求子矩阵异或和板子。
- 仿照子矩阵加,求子矩阵加的形式,设 \(d_{i,j}=a_{i,j} \bigoplus a_{i-1,j} \bigoplus a_{i,j-1} \bigoplus a_{i-1,j-1}\) 表示差分数组。
- 此时左上角为 \((x_{1},y_{1})\) ,右下角为 \((x_{2},y_{2})\) 的子矩阵异或就转化为了 \((x_{1},y_{1}),(x_{2}+1,y_{2}+1),(x_{2}+1,y_{1}),(x_{1},y_{2}+1)\) 的单点异或。
- 若为子矩阵加,则 \((x_{1},y_{1}),(x_{2}+1,y_{2}+1)\) 加上 \(val\) , \((x_{2}+1,y_{1}),(x_{1},y_{2}+1)\) 减去 \(val\) 。
- 而子矩阵异或和 \(\bigoplus\limits_{x=x_{1}}^{x_{2}}\bigoplus\limits_{y=y_{1}}^{y_{2}}a_{x,y}=(\bigoplus\limits_{x=1}^{x_{1}-1}\bigoplus\limits_{y=1}^{y_{1}-1}a_{x,y}) \bigoplus (\bigoplus\limits_{x=1}^{x_{1}-1}\bigoplus\limits_{y=1}^{y_{2}}a_{x,y}) \bigoplus (\bigoplus\limits_{x=1}^{x_{2}}\bigoplus\limits_{y=1}^{y_{1}-1}a_{x,y}) \bigoplus (\bigoplus\limits_{x=1}^{x_{2}}\bigoplus\limits_{y=1}^{y_{2}}a_{x,y})\) ,不妨先解决形如 \(\bigoplus\limits_{x=1}^{n}\bigoplus\limits_{y=1}^{m}a_{x,y}\) 的问题。
- 将差分展开,有 \(\bigoplus\limits_{x=1}^{n}\bigoplus\limits_{y=1}^{m}a_{x,y}=\bigoplus\limits_{x=1}^{n}\bigoplus\limits_{y=1}^{m}\bigoplus\limits_{i=1}^{x}\bigoplus\limits_{j=1}^{y}d_{i,j}\) 。观察到 \(d_{i,j}\) 被异或了 \((n-i+1)(m-j+1)\) 遍,此时 \(d_{i,j}\) 只会在 \((n-i+1)(m-j+1)\) 为奇数即 \(n,i\) 奇偶性相同且 \(m,j\) 奇偶性相同才会产生贡献。
- 开四个树状数组分别维护横、纵左边奇偶性即可。
点击查看代码
struct BIT { ll c[2][2][1010][1010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll m,ll x,ll y,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) { for(ll j=y;j<=m;j+=lowbit(j)) { c[x&1][y&1][i][j]^=val; } } } ll getsum(ll x,ll y) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { for(ll j=y;j>=1;j-=lowbit(j)) { ans^=c[x&1][y&1][i][j]; } } return ans; } void update(ll n,ll m,ll x1,ll y1,ll x2,ll y2,ll val) { add(n,m,x1,y1,val); add(n,m,x2+1,y2+1,val); add(n,m,x2+1,y1,val); add(n,m,x1,y2+1,val); } ll query(ll x1,ll y1,ll x2,ll y2) { return getsum(x1-1,y1-1)^getsum(x1-1,y2)^getsum(x2,y1-1)^getsum(x2,y2); } }B; int main() { ll n,m,pd,x1,y1,x2,y2,val,i; scanf("%lld%lld",&n,&m); for(i=1;i<=m;i++) { scanf("%lld%lld%lld%lld%lld",&pd,&x1,&y1,&x2,&y2); if(pd==1) { printf("%lld\n",B.query(x1,y1,x2,y2)); } else { scanf("%lld",&val); B.update(n,n,x1,y1,x2,y2,val); } } return 0; }
[ABC366G] XOR Neighbors
-
观察到 \(n \le 60\) ,考虑高斯消元-回代消元。
-
难点在于自由元的处理和无解的判定。自由元因为值域很大,类似 luogu P3164 [CQOI2014] 和谐矩阵 直接
rand()
处理即可;无解和普通高斯消元一样,或者判一下构造出的解中是否有 \(0\) 。点击查看代码
mt19937 rng(time(0)); ll a[70][70],ans[70]; int main() { ll n,m,u,v,val,flag=0,i,j,k; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v; a[u][v]=a[v][u]=1; } for(i=1;i<=n;i++) { val=i; for(j=i+1;j<=n;j++) { if(a[j][i]==1) { val=j; break; } } for(j=1;j<=n+1;j++) { swap(a[i][j],a[val][j]); } if(a[i][i]!=0) { for(j=i+1;j<=n;j++) { if(a[j][i]==1) { for(k=1;k<=n;k++) { a[j][k]^=a[i][k]; } } } } } for(i=n;i>=1;i--) { if(a[i][i]==0) { ans[i]=rng()+1; } else { ans[i]=a[i][n+1]; for(j=i+1;j<=n;j++) { if(a[i][j]==1) { ans[i]^=ans[j]; } } } flag|=(ans[i]==0); } if(flag==1) { cout<<"No"<<endl; } else { cout<<"Yes"<<endl; for(i=1;i<=n;i++) { cout<<ans[i]<<" "; } } return 0; }
luogu P10484 送礼物
-
meet in middle /折半搜索/双向搜索板子。
- 将搜索区间 \([1,n]\) 分成 \([1,mid]\) 和 \([mid+1,n]\) 两部分,并记录两次搜索的答案。此时的时间复杂度为 \(O(2^{\frac{n}{2}+1})\) 。
- 用
set
二分合并答案一般情况下是足够的;如果卡常的话,那就排序加去重后二分或双指针维护。
点击查看代码
ll w[50],a[(1<<23)+10],b[(1<<23)+10]; void dfs(ll pos,ll n,ll m,ll sum,ll a[]) { if(pos==n+1) { a[0]++; a[a[0]]=sum; } else { if(sum+w[pos]<=m) { dfs(pos+1,n,m,sum+w[pos],a); } dfs(pos+1,n,m,sum,a); } } int main() { ll n,m,ans=0,i,l,r; cin>>m>>n; for(i=1;i<=n;i++) { cin>>w[i]; } dfs(1,n/2,m,0,a); dfs(n/2+1,n,m,0,b); sort(a+1,a+1+a[0]); sort(b+1,b+1+b[0]); a[0]=unique(a+1,a+1+a[0])-(a+1); b[0]=unique(b+1,b+1+b[0])-(b+1); for(l=1,r=b[0];l<=a[0];l++) { while(r>=1&&a[l]+b[r]>m) { r--; } ans=max(ans,a[l]+b[r]); } cout<<ans<<endl; return 0; }
10.18
闲话
- 早上起床时还在下雨,遂直接来机房了。
- 过了一会儿 \(huge\) 问我们今天不是让回班上早读吗,我们说昨天晚上 \(feifei\) 说让今天在机房上早读,明天再回教室上早读。然后我们跟 \(huge\) 说回班上早读的话,就得 \(7:10\) 才去吃饭,就赶不上 \(7:30\) 的模拟赛了,而且 \(whk\) 资料已经全搬到机房了,然后 \(huge\) 说那我们以后就在机房上早读,然后又劝了劝我们要好好上早读,语文背背课本的古文、古诗,英语背背单词、课文。我们开始早读时 \(huge\) 还一会儿拿着英语课本、一会儿拿着语文课本在两个机房来回溜达。
- 上午 \(7:30 \sim 11:30\) 和 \(GXYZ\) 的打学校模拟赛。
- 下午放的是 @GGrun 的每日一歌《On My Own》;然后和 \(GXYZ\) 的讲题,讲题前还以 @dingzibo_______ 现在才初二 \(D\) 了我们。
- 因明后两天教室被征用做成人高考考场了,所以 \(feifei\) 说晚上 \(19:55\) 班主任让我们回班收拾东西,我们本想以教室里已经没东西可收拾了就不去了,结果 \(feifei\) 说我们还得回去干活,回班后班主任说我们收拾完了就赶紧回去,故我们白跑了一趟,话说 \(feifei\) 咋越来越唐了。
做题纪要
luogu P9234 [蓝桥杯 2023 省 A] 买瓜
T2918. 小 h 的几何
T2921. 小j 的组合
luogu B4042 [语言月赛 202410] 顺序结构
luogu B4043 [语言月赛 202410] 刻度尺
luogu B4044 [语言月赛 202410] 奇迹战神
luogu B4045 [语言月赛 202410] 同桌
luogu B4046 [语言月赛 202410] 寻找质数
luogu B4047 [语言月赛 202410] 校门外的施工
luogu B4048 [语言月赛 202410] 断章取义
luogu B4049 [语言月赛 202410] 平均分计算
CF525E Anya and Cubes
[AGC026C] String Coloring
-
由题有 \(\begin{cases} red_{1,mid}+red_{mid+1,n}=blue_{1,mid}+blue_{mid+1,n}=n \\ red_{1,mid}+blue_{1,mid}=red_{mid+1,n}+blue_{mid+1,n}=n \end{cases}\) ,从而得到 \(\begin{cases} red_{1,mid}=blue_{mid+1,n} \\ blue_{1,mid}=red_{mid+1,n} \end{cases}\) ,相应地得到前半段红串是后半段蓝串的反串,前半段蓝串是后半段红串的反串。
点击查看代码
ll ans=0; char s[50]; string blue,red; map<pair<string,string>,ll>f; void dfs1(ll pos,ll n) { if(pos==n+1) { f[make_pair(blue,red)]++; } else { blue.push_back(s[pos]); dfs1(pos+1,n); blue.pop_back(); red.push_back(s[pos]); dfs1(pos+1,n); red.pop_back(); } } void dfs2(ll pos,ll n) { if(pos==n+1) { reverse(blue.begin(),blue.end()); reverse(red.begin(),red.end()); if(f.find(make_pair(red,blue))!=f.end()) { ans+=f[make_pair(red,blue)]; } reverse(blue.begin(),blue.end()); reverse(red.begin(),red.end()); } else { blue.push_back(s[pos]); dfs2(pos+1,n); blue.pop_back(); red.push_back(s[pos]); dfs2(pos+1,n); red.pop_back(); } } int main() { ll n; scanf("%lld%s",&n,s+1); dfs1(1,n); dfs2(n+1,2*n); cout<<ans<<endl; return 0; }
10.19
闲话
- 侯操时因地面还没干,所以原要去大操场进行的会操比赛取消了,到原集合地点集合。德育主任没说让跑操,直接让去食堂分“自习室”,然后回宿舍收拾内务加拿书并在 \(7:10\) 出宿舍去食堂吃饭;貌似我们班班主任以食堂不是学习的地方,所以直接带着去滏阳楼了。遂我们直接去机房了。
- 到机房后 \(field\) 说让我们上早读,因为没有早读任务所以上语文早读。但 \(field\) 没像昨天 \(huge\) 一样把我们电脑锁屏了然后跟我们一起上早读,而是自己坐在前面教师机的位置,象征性地看着我们上早读。
- 吃完饭到机房后 \(field\) 问我们都有手机吗,说下午打信友队模拟赛还需要临时注册账号。
- 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。打完模拟赛就把手机发下来了,让我们去注册账号,因不知道自己手机号是啥所以又给家里打了个电话问下,然后把手机交回去就可以去上体活了。
- 下午 \(14:30 \sim 18:30\) 打信友队的模拟赛。
- 晚上尝试听信友队模拟赛的讲评但还要讲 \(J\) 组,所以让我们先讲上午模拟赛的题(多校讲题时和信友队模拟赛冲突了)。但在我们讲完上午模拟赛题时,信友队模拟赛的讲评才刚讲到 \(J\) 组 \(T4\) ,然后 \(field\) 说让我们先去打 ABC ,他帮我们录着屏明天再听。
- 晚上 \(20:00 \sim 21:40\) 打 ABC 。
做题纪要
P382. 排列最小生成树 (pmst)
P383. 卡牌游戏 (cardgame)
[ABC376A] Candy Button
[ABC376B] Hands on Ring (Easy)
[ABC376C] Prepare Another Box
[ABC376D] Cycle
[ABC376E] Max × Sum
10.20
闲话
- 因时间和高二保持一致,所以早上原到 \(7:50\) 结束的体活改到了 \(7:30\) 结束。
- 到机房后 \(miaomiao\) 说今天没有模拟赛,让我们把这一周的题好好改改;并得知 \(huge,field\) 解决 luogu 封学校 IP 的方法是通过一些手段切换检测到的我们电脑的 IP ,比较逆天。
- 下午起床后下雨了。
- 下午放 @YWHHDJSer 的每日一歌《白复生》;然后就被 \(miaomiao\) 拉去和隔壁高二的一起宣誓(之前都是高二的单独宣誓)。
- 详见 2024 CSP-S 游记 10.20 。
- 晚上貌似有一调总结大会,但我们不用去听。
做题纪要
P384. 比特跳跃 (jump)
luogu P11204 「Cfz Round 9」Lone
-
容易想到的一种可能的构造方法是 \(n- m \bmod n\) 个 \(\left\lfloor \frac{m}{n} \right\rfloor\) 和 \(m \bmod n\) 个 \(\left\lfloor \frac{m}{n} \right\rfloor+1\) 。
-
在 \(\left\lfloor \frac{m}{n} \right\rfloor=1\) 时进行特判 \(n-m \bmod n=1\) 时有解即可。
点击查看代码
int main() { ll t,n,m,i; cin>>t; for(i=1;i<=t;i++) { cin>>n>>m; if(m==n||m/n>=2) { cout<<"Yes"<<endl; } else { if(n-m%n==1) { cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } } } return 0; }
luogu P11205 「Cfz Round 9」Hope
-
询问等价于求 \(\operatorname{mex}\) 。
-
最后得到的极长值域序列一定形如 \(1,2,3,\dots k\) 。
-
排序后直接做即可。
点击查看代码
ll a[100010]; int main() { ll t,n,ans,sum1,sum2,i,j; cin>>t; for(j=1;j<=t;j++) { ans=1; sum1=sum2=0; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n); for(i=1;i<=n;i++) { if(a[i]>=ans) { sum1+=a[i]-ans; ans++; } else { sum2+=a[i]-1; } } cout<<ans+(sum1<=ans&&ans<=sum1+sum2)<<endl; } return 0; }
2024CSP-S第二轮(复赛)模拟赛 A. 坦白
信友队2024CSP-S第二轮(复赛)模拟赛 B. 秘密
LibreOJ 193.线段树历史和
- 多倍经验: luogu U216697 线段树区间历史版本和
- 线段树维护区间历史版本和板子。
- 非矩阵写法
-
设当前节点维护的区间为 \([l,r]\) ,长度为 \(len\) ,区间和 \(sum\) ,区间历史和 \(hsum\) ,当前时间/求历史和轮数为 \(t\) ,加法懒惰标记 \(lazy_{i \in [1,t]}\) 表示 \(i\) 时刻的懒惰标记,懒惰标记历史和 \(hlazy=\sum\limits_{i=1}^{t}lazy_{i}\) 。
-
此时有 \(hsum=\sum\limits_{i=1}^{t}(sum+len \times lazy_{i})=sum \times t+hlazy \times len\) 。
-
将操作分成修改/询问操作和求一次历史和两部分。这样的话就需要额外记录 \(t\) 的懒惰标记 \(tlazy\) 。
-
区间加上 \(val\) 时,更新为 \(\begin{cases}sum \gets sum+val \times len \\ lazy_{t} \gets lazy_{t-1}+val \end{cases}\) 。
- 从矩阵的角度分析(先别管懒惰标记),等价于 \(\begin{bmatrix} len & sum & hsum \end{bmatrix} \times \begin{bmatrix} 1 & val & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix}=\begin{bmatrix} len & sum+val \times len & hsum \end{bmatrix}\) 。
-
当时间 \(t\) 加 \(1\) 时表示求一次历史和。更新为 \(\begin{cases} hsum \gets hsum+sum \times \Delta t+hlazy \times len \\ hlazy \gets hlazy+lazy_{t} \times \Delta t \\ tlazy \gets tlazy+\Delta t \end{cases}\) 。
- 从矩阵的角度分析(先别管懒惰标记),等价于 \(\begin{bmatrix} len & sum & hsum \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & \Delta t \\ 0 & 0 & 1 \end{bmatrix}=\begin{bmatrix} len & sum & hsum+sum \times \Delta t \end{bmatrix}\) 。
-
加上懒惰标记后,即 \(\begin{bmatrix} len & sum & hsum \end{bmatrix} \times \begin{bmatrix} 1 & lazy & hlazy \\ 0 & 1 & \Delta t \\ 0 & 0 & 1 \end{bmatrix}=\begin{bmatrix} len & sum+lazy \times len & hsum+sum \times \Delta t+hlazy \times len \end{bmatrix}\) 。
点击查看代码
ll a[100010]; struct SMT { struct SegmentTree { ll len,sum,hsum,lazy,hlazy,tlazy; }tree[400010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; tree[rt].hsum=tree[lson(rt)].hsum+tree[rson(rt)].hsum; } void build(ll rt,ll l,ll r) { tree[rt].len=r-l+1; if(l==r) { tree[rt].sum=tree[rt].hsum=a[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushlazy(ll rt,ll lazy,ll hlazy,ll tlazy) { tree[rt].hsum+=tree[rt].sum*tlazy+tree[rt].len*hlazy; tree[rt].sum+=lazy*tree[rt].len; tree[rt].hlazy+=tree[rt].lazy*tlazy+hlazy; tree[rt].lazy+=lazy; tree[rt].tlazy+=tlazy; } void pushdown(ll rt) { pushlazy(lson(rt),tree[rt].lazy,tree[rt].hlazy,tree[rt].tlazy); pushlazy(rson(rt),tree[rt].lazy,tree[rt].hlazy,tree[rt].tlazy); tree[rt].lazy=tree[rt].hlazy=tree[rt].tlazy=0; } void update_val(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { tree[rt].sum+=val*tree[rt].len; tree[rt].lazy+=val; return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) { update_val(lson(rt),l,mid,x,y,val); } if(y>mid) { update_val(rson(rt),mid+1,r,x,y,val); } pushup(rt); } void update_tim(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { tree[rt].hsum+=tree[rt].sum*val+tree[rt].len*tree[rt].hlazy; tree[rt].hlazy+=tree[rt].lazy*val; tree[rt].tlazy+=val; return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) { update_tim(lson(rt),l,mid,x,y,val); } if(y>mid) { update_tim(rson(rt),mid+1,r,x,y,val); } pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) { return tree[rt].hsum; } pushdown(rt); ll mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt),mid+1,r,x,y); } return ans; } }T; int main() { ll n,m,pd,l,r,x,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } T.build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { cin>>x; T.update_val(1,1,n,l,r,x); } else { cout<<T.query(1,1,n,l,r)<<endl; } T.update_tim(1,1,n,1,n,1); } return 0; }
-
- 矩阵写法
- 因进行了多次无用计算,故常数较大,可以展开手动计算。
- 正统的矩阵写法需要把 \(lazy,hlazy,tlazy\) 也扔进初始矩阵里。
- 非矩阵写法
luogu P4314 CPU 监控
-
难点在于如何求历史最大值。
-
考虑记录最大值 \(maxx\) ,历史最大值 \(hmaxx\) ,加法标记 \(add\) ,历史(可行范围内)最大加法标记 \(hadd\) ,覆盖懒惰标记 \(col\) ,历史(可行范围内)最大覆盖标记 \(lcol\) 。
-
第一次覆盖操作后的加法都可以看做覆盖。
-
加法通过记录的 \(hadd\) 和已知的 \(maxx\) 更新 \(hmaxx\) ;覆盖正常做就行了。
-
pushdown
判断是否有加法标记下传比较难做,直接不管,狂暴下传。点击查看代码
const int inf=0x7f7f7f7f; int a[100010]; struct SMT { struct SegmentTree { int maxx,hmaxx,add,hadd,col,hcol; }tree[400010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); tree[rt].hmaxx=max(tree[lson(rt)].hmaxx,tree[rson(rt)].hmaxx); } void build(int rt,int l,int r) { tree[rt].col=tree[rt].hcol=-inf; if(l==r) { tree[rt].maxx=tree[rt].hmaxx=a[l]; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushcol(int rt,int col,int hcol) { tree[rt].add=0; tree[rt].col=tree[rt].maxx=col; tree[rt].hmaxx=max(tree[rt].hmaxx,hcol); tree[rt].hcol=max(tree[rt].hcol,hcol); } void pushadd(int rt,int add,int hadd) { if(tree[rt].col!=-inf) { pushcol(rt,tree[rt].col+add,tree[rt].col+hadd); } else { tree[rt].hadd=max(tree[rt].hadd,tree[rt].add+hadd); tree[rt].hmaxx=max(tree[rt].hmaxx,tree[rt].maxx+hadd); tree[rt].add+=add; tree[rt].maxx+=add; } } void pushdown(int rt) { pushadd(lson(rt),tree[rt].add,tree[rt].hadd); pushadd(rson(rt),tree[rt].add,tree[rt].hadd); tree[rt].add=tree[rt].hadd=0; if(tree[rt].col!=-inf) { pushcol(lson(rt),tree[rt].col,tree[rt].hcol); pushcol(rson(rt),tree[rt].col,tree[rt].hcol); tree[rt].col=tree[rt].hcol=-inf; } } void updatep(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { pushadd(rt,val,val); return; } pushdown(rt); int mid=(l+r)/2; if(x<=mid) { updatep(lson(rt),l,mid,x,y,val); } if(y>mid) { updatep(rson(rt),mid+1,r,x,y,val); } pushup(rt); } void updatec(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { pushcol(rt,val,val); return; } pushdown(rt); int mid=(l+r)/2; if(x<=mid) { updatec(lson(rt),l,mid,x,y,val); } if(y>mid) { updatec(rson(rt),mid+1,r,x,y,val); } pushup(rt); } int queryq(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt].maxx; } pushdown(rt); int mid=(l+r)/2,ans=-inf; if(x<=mid) { ans=max(ans,queryq(lson(rt),l,mid,x,y)); } if(y>mid) { ans=max(ans,queryq(rson(rt),mid+1,r,x,y)); } return ans; } int querya(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt].hmaxx; } pushdown(rt); int mid=(l+r)/2,ans=-inf; if(x<=mid) { ans=max(ans,querya(lson(rt),l,mid,x,y)); } if(y>mid) { ans=max(ans,querya(rson(rt),mid+1,r,x,y)); } return ans; } }T; int main() { int n,m,l,r,val,i; char pd; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } T.build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd=='Q') { cout<<T.queryq(1,1,n,l,r)<<endl; } if(pd=='A') { cout<<T.querya(1,1,n,l,r)<<endl; } if(pd=='P') { cin>>val; T.updatep(1,1,n,l,r,val); } if(pd=='C') { cin>>val; T.updatec(1,1,n,l,r,val); } } return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18457214,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。