2019.9.24 csp-s模拟测试51(a) 反思总结
T1:还在头铁,顺便复习了一下lct【虽然这题用不上因为复杂度不对】
头铁结束。
虽然题目存在换根的操作,实际上并不用真的换根。
2操作中求lca的时候只要考虑原树上root和x、y的lca以及x,y的lca,三个中取最深的就是现树上x和y的lca。
关于u的子树整体操作需要分类讨论。如果现根不在原树上u的子树里,那么在新树上的目标子树与原树相同,直接操作。如果u就是root,那么直接整棵树都操作。最后如果root在原树上u子树里,那么需要反一下,应当进行操作的部分是整棵树上除了u包含root的这棵子树的所有部分。
查询操作同理。
树上一棵子树的整体加减可以利用树剖解决。一整棵子树对应在线段树上是一段连续区间。
#include<iostream>
#include<cstdio>
using namespace std;
int n,q,a[],root=,t;
int ver[],Next[],head[],tot;
int siz[],dep[],son[],rec[],rec1[];
int fa[],lip[],cnt;
void add(int x,int y){
ver[++tot]=y;
Next[tot]=head[x];
head[x]=tot;
}
struct node{
int l,r;
long long sum,tag;
}b[*];
void dfs(int x,int f){
siz[x]=;
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(y==f)continue;
dep[y]=dep[x]+;
fa[y]=x;
dfs(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
}
void dfs1(int x,int upp){
lip[x]=upp;
rec[x]=++cnt;
rec1[cnt]=x;
if(!son[x])return;
dfs1(son[x],upp);
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(y==fa[x]||y==son[x])continue;
dfs1(y,y);
}
}
void build(int p,int l,int r){
b[p].l=l,b[p].r=r;
if(l==r){
b[p].sum=a[rec1[l]];
return;
}
int mid=(l+r)/;
build(p*,l,mid);
build(p*+,mid+,r);
b[p].sum=b[p*].sum+b[p*+].sum;
}
int lca(int x,int y){
while(lip[x]!=lip[y]){
if(dep[lip[x]]<dep[lip[y]])swap(x,y);
x=fa[lip[x]];
}
if(dep[x]>dep[y])return y;
else return x;
}
int lca1(int x,int y){
while(lip[x]!=lip[y]){
if(fa[lip[y]]==x)return lip[y];
y=fa[lip[y]];
}
return rec1[rec[x]+];
}
void pushdown(int p){
if(b[p].tag){
b[p*].tag+=b[p].tag;
b[p*].sum+=(b[p*].r-b[p*].l+)*b[p].tag;
b[p*+].tag+=b[p].tag;
b[p*+].sum+=(b[p*+].r-b[p*+].l+)*b[p].tag;
b[p].tag=;
}
}
void change(int p,int l,int r,long long y){
if(l<=b[p].l&&b[p].r<=r){
b[p].sum+=(b[p].r-b[p].l+)*y;
b[p].tag+=y;
return;
}
pushdown(p);
int mid=(b[p].l+b[p].r)/;
if(l<=mid)change(p*,l,r,y);
if(r>mid)change(p*+,l,r,y);
b[p].sum=b[p*].sum+b[p*+].sum;
}
long long ask(int p,int l,int r){
if(l<=b[p].l&&b[p].r<=r){
return b[p].sum;
}
pushdown(p);
int mid=(b[p].l+b[p].r)/;
long long val=;
if(l<=mid)val+=ask(p*,l,r);
if(r>mid)val+=ask(p*+,l,r);
b[p].sum=b[p*].sum+b[p*+].sum;
return val;
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d%d",&n,&q);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(,);
dfs1(,);
build(,,n);
// scanf("%d",&q);
for(int i=,opt,x,y;i<=q;i++){
long long z;
scanf("%d",&opt);
if(opt==){
scanf("%d",&x);
root=x;
}
else if(opt==){
scanf("%d%d%lld",&x,&y,&z);
int x1=lca(x,y);
t=x1;
int x2=lca(x,root);
if(dep[x2]>dep[t])t=x2;
int x3=lca(y,root);
if(dep[x3]>dep[t])t=x3;
if(lca(t,root)!=t){//t原子树
change(,rec[t],rec[t]+siz[t]-,z);
}
else if(t==root){
b[].sum+=n*z;
b[].tag+=z;
}
else{
b[].sum+=n*z;
b[].tag+=z;
int t1=lca1(t,root);
change(,rec[t1],rec[t1]+siz[t1]-,-z);
}
}
else{
scanf("%d",&x);
t=x;
if(lca(t,root)!=t){//t原子树
printf("%lld\n",ask(,rec[t],rec[t]+siz[t]-));
}
else if(t==root){
printf("%lld\n",b[].sum);
}
else{
long long val=b[].sum;
int t1=lca1(t,root);
val-=ask(,rec[t1],rec[t1]+siz[t1]-);
printf("%lld\n",val);
}
}
}
return ;
}
T2:Function
看到题目里的递推式,莫名其妙先画了个网格,横x轴纵y轴。
然后发现第一排是a1的各个倍数,第一列是ai自己。然后根据相邻两个a的大小关系好像可以推路径。
然后写暴力打了个表,按普通的行列输出,发现自己这部分想麻烦了。似乎前面小的ai可以斜着连过来覆盖现在的ai,一列会分段被多个ai覆盖。然后感觉有点像决策单调性【什么】,试图从前面转移。
然后考虑给一列打标记,在哪个x开始被前面的哪个a覆盖,然后每一列从上一列转移这个标记。然后发现中间有断层,行不通。
这个时候重新开始想之前发现的斜着连过来这一块,一个(x,y)可以先向之前的ai的列去走,然后到某一列一直走到顶。
于是怎么找这个ai?
把走到ai这列得出的答案列了个式子:ai*(x-y)+s[y]-s[i]+i*a[i]。假设从(x,y)往回一直走到第一列,还剩下x-y步到顶,这部分一定要填ai。然而ai不一定是第一列,多往回走几列就少让ai累加了多少行,再把这部分补上。以及第一行就是ai本身而不是从0开始所以相当于ai多累加一行。
对于一个询问,sy是不变的,所以先去掉。发现剩下的这部分式子是一个关于x-y的一次函数。假设a=ai,b=-s[i]+i*a[i]。
我对斜率优化不熟…在这里开始懵逼了,最后强行头铁出了单调栈这一步但是写不完了。
/*(注释掉了)
推出一次函数以后,考虑ai比aj优的条件。两个式子用不等式算一下得出i比j优的条件是(bj-bi)/(ai-aj)>(x-y),两个ai覆盖的交点也可以这样得到。
维护一个下凸包,斜率递增。
观察打好的表发现,覆盖同一列y的ai从靠近y的地方递减。离y远而a更大的ai没有再做贡献的可能性,即作贡献的ai递减。
所以可以用单调栈维护。询问的时候在凸壳上二分x的位置。
*/
出锅了出锅了,方法没锅思路有锅。
之前有点强行解释的嫌疑。实际上得出一堆一次函数以后,因为我们只考虑这些一次函数的最小值,所以才会去维护一个上凸包。
上凸包!!被机房大佬无情嘲笑。画张图一看我脑子里这就是个上凸包…
用两个点的函数式进行比较的函数单纯只是为了维护这个凸包而已和什么x的覆盖到底有没有关系我也搞不清。但是不用这种写法,老老实实在凸包上二分,当然完全是正确的。
二次补充:
的确是上凸包,这个没问题。
每次到新的一列加一条直线,其实是加了一条从原点开始斜率ai的直线。因为每次转移到下一列的时候凸包其实整体向右和向上平移了【可能就是我前面发现的多端覆盖标记的后移】,所以对于新加入的这条原点开始的直线来说,它能ban掉所有之前的斜率比它大的直线,即aj>ai且j<i的直线。
这就是弹出大于当前ai的a值的意义。
而后面利用函数比较而弹栈的操作,有一种更好理解的做法【也许等价?】:解方程得栈顶与新加入直线的交点以及栈顶与前一条直线的交点,弹掉所有会让凸包交点不单调的直线。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,q,cnt=;
long long a[],sum[],b[],ans[];
double num[];
int t[],p;
struct node{
int x,y,id;
}que[];
bool cmp(node a,node b){
if(a.y==b.y)return a.x<b.x;
else return a.y<b.y;
}
double get(int x,int y){
return (double)(b[y]-b[x])/(double)(a[x]-a[y]);
}
int main()
{
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-]+a[i];
b[i]=i*a[i]-sum[i];
}
scanf("%d",&q);
for(int i=;i<=q;i++){
scanf("%d%d",&que[i].x,&que[i].y);
que[i].id=i;
}
sort(que+,que+q+,cmp);
for(int i=;i<=n;i++){
while(p&&a[t[p]]>=a[i])p--;
while(p>=&&get(i,t[p])>get(t[p],t[p-]))p--;
t[++p]=i;
if(p>)num[n-p]=get(i,t[p-]);
while(que[cnt].y==i){
int x=lower_bound(num+n-p,num+n-,(que[cnt].x-que[cnt].y))-num;
x=n-x;
ans[que[cnt].id]=a[t[x]]*(que[cnt].x-que[cnt].y)+b[t[x]]+sum[que[cnt].y];
cnt++;
}
}
for(int i=;i<=q;i++){
printf("%lld\n",ans[i]);
}
return ;
}
有些地方自己也不是很明白,再找一点斜率优化的题写…
二次补充结束,终于算是全搞明白了……
T3:什么FT?FF什么?F什么T?
因为头铁T2而挂掉了的考试,最后发现T2其实我也没推出来多少,主要是自创斜率优化【指不熟斜率优化】的操作属实弟中弟。
今天没有考试,但我改不完【迫真】
2019.9.24 csp-s模拟测试51(a) 反思总结的更多相关文章
- 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组
2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)
2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...
- 2019.10.24 CSP%你赛第二场d1t3
题目描述 Description 精灵心目中亘古永恒的能量核心崩溃的那一刻,Bzeroth 大陆的每个精灵都明白,他们的家园已经到了最后的时刻.就在这危难关头,诸神天降神谕,传下最终兵器——潘少拉魔盒 ...
- [考试反思]0924csp-s模拟测试51:破碎
总参赛人数:15 有点菜. 不知道是撞了什么大运没有滚出A层. 但是一回到A层就暴露出了一个大问题:码速. 不是调试速度,,就是纯粹码的速度... 边讲考试状态边说吧... 上来肝T1.一看,是个换根 ...
- CSP-S 模拟测试 51 题解
考试过程: 惯例先看一遍三道题,T1 一开始反应要求割点,但是这是有向图,肯定不能求割点,康了一下数据范围,有40%是树的,还不错,决定待会在打. 看T2 字符串题,完了我字符串最弱了,肯定只能打暴力 ...
- [CSP-S模拟测试51]题解
错失人生中第一次AK的机会…… A.attack 支配树板子题.考场上发明成功√ 首先支配树上两点路径之间的点都是必经之点,根据这个性质我们就可以yy出建树的方法.跑拓扑,在每个点(设为$x$)即将入 ...
- csp-s模拟测试51(b)attack,tree题解
题面:https://www.cnblogs.com/Juve/articles/11598286.html attack: 支配树裸题? 看一下支配树是什么: 问题:我们有一个有向图(可以有环),定 ...
- 2019.10.20 csp-s模拟测试 lrd试题 反思总结
赶进度赶进度,丢个代码两三句备注一下完事了. day1: 前面两道题没实际写代码怕印象不深所以描述一下大意. T1: 题目大意:给出两个数&.|.^的结果(可能只给出其中某一项或者某两项),求 ...
随机推荐
- Python更新后ros用不了的bug
一.原因 我同时安装了python2.7 和3.5,而且将python默认配置为python3.5,所以ros并不支持,所以提示找不到. 2.解决方式 通过修改不同版本的python的优先级,将pyt ...
- sql.xml大于小于号处理的方法
<if test="startTime != null and startTime != ''"> AND i_DataTime <![CDATA[ >= ...
- C#控件的闪烁问题解决方法总结
最近对代码作了一些优化,试验后效果还可以,但是发现界面会闪烁,具体是TreeView控件会闪烁,语言为C#,IDE为VS2005.在查阅一些资料,使用了一些基本技术后(如开启双缓冲),发现没什么效果. ...
- Redis Set ZSet类型的学习
- winDbg + VMware + window 双机联调环境搭建
这里简单的介绍一下内核开发双机联调的搭建环境,尽管网上有很多类似的文章,但看了很多总是不太舒服,觉得不太明白,所以自己实践一下总结一篇.下面就拿我的环境简单介绍,希望别人可以看懂. 准备工具:装虚拟机 ...
- php 网页内容抓取
最近抓的2个网站内容的代码 列表页抓取:第一种使用phpquery插件,可以快速获取,第二种它是api,所以直接获取 load_third("phpQuery.php"); /** ...
- vector以及array和数组
//比较数组.vector.array #include <iostream> #include <vector> #include <array> #includ ...
- HZOI20190828模拟32题解
题面:https://www.cnblogs.com/Juve/articles/11428730.html chinese: 考虑$\sum\limits_{i=0}^{n*m}i*f_i$的意义: ...
- Ionic JPush极光推送二
1.看图解决问题 2.解决出现统计代码提示问题 修改这个java 文件 导入命名空间 import cn.jpush.android.api.JPushInterface; 添加方法 @Overr ...
- Cesium 1.51新功能评测
前言 之前介绍Cesium1.50版本的新功能时,很多人把1.50写成1.5.这两个版本可不一样,之间差了45个小版本号,1.5版本大概是Cesium三年前的版本了. Cesium每月月初的第一个工作 ...