[NOIP10.6模拟赛]2.equation题解--DFS序+线段树
题目链接:
咕
闲扯:
终于在集训中敲出正解(虽然与正解不完全相同),开心QAQ
首先比较巧,这题是\(Ebola\)出的一场模拟赛的一道题的树上强化版,当时还口胡出了那题的题解
然而考场上只得了86最后一个substask被卡了,一开始以为毒瘤出题人卡常(虽然真卡了)卡线段树,题目时限1.5s,评测机上两个点擦线1500ms左右,剩下两个点不知道。然后本地测一下都是1900+ms!机子性能已经这样了吗....结果把快读换成\(fread\),TM过了!最慢的1200+ms!!!这......无话可说,\(getchar()\)快读也卡讲究
分析:
首先最简单的处理不讲了.就是把每个点的未知数表示成\(k_i x_1 + b_i\)的形式,这DFS一遍就好了
然后观察到有一个1e3的子任务,想想暴力怎么做,我们对于操作1,相当于\((k_i+k_j)x_1+(b_i+b_j)=w\)判断一下解得情况就好了,\(O(1)\)完成;
对于操作2,我们可以发现对于\(x\)的操作,只会对\(x\)的子树中的\(k_ix_1+b_i\)形式有影响(实际上只会影响\(b_i\)),于是\(DFS\)一遍子树即可,这样总的暴力时间复杂度是\(O(nq)\)
考虑优化暴力,
我们发现瓶颈是操作2,如果将\(x\)与其父亲的边权从\(w_1\)改为\(w_2\),那么加入\(x\)本来形式是\(k_ix_1+b_i\),这时候变成了\(k_i x_1+b_i+w_2-w_1\),相当于加操作,当时在\(x\)的子树中与\(x\)的\(k_i\)(实际上显然只有-1,1两种取值)不同的点,\(b\)值却应该减去\(w_2-w_1\),所以我们将标记开成一个二元组,一个记录标记的正负,另一个记录值,重载下运算符就很方便了
struct Tag{
int o;//标记的正负
ll dt;
Tag(){o=dt=0;}
Tag(int o){o=dt=o;}
Tag(int _o,ll _dt){o=_o,dt=_dt;}
Tag operator +(const Tag &b)const{
Tag tmp=*this;
if(tmp.o==0)tmp=b;
else if(b.o==0)return tmp;
else {
if(o!=b.o){
tmp.dt=dt-b.dt;
}
else tmp.dt=dt+b.dt;
}
return tmp;
}
};
这样对于操作2,只用在子树加个标记就好了,因为dfs序是一段连续区间(我比较傻考场上是用树链剖分)使用线段树就好了
对于操作1,我们两次单点查询就好了,然后按暴力那样处理.
总的时间复杂度\(O(q log N)\),常数稍大
当然标算std是将深度分奇偶考虑,然后树状数组维护差分标记,时间复杂度相同但是常数小的多
代码
这是考场代码换了快读,如果想看线段树部分直接跳到\(niconicoi\)那个\(namespace\)就好了
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#define ll long long
#define ri register int
using std::min;
using std::abs;
using std::max;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
x=0;int ne=0;char c;
while(!isdigit(c=nc()))ne=c=='-';
x=c-48;
while(isdigit(c=nc()))x=(x<<3)+(x<<1)+c-48;
x=ne?-x:x;return ;
}
const int maxn=100005;
const int inf=0x7fffffff;
const int N=1000005;
struct Edge{
int ne,to;
ll dis;
}edge[N<<1];
int h[N],num_edge=1;
inline void add_edge(int f,int to,int c){
edge[++num_edge].ne=h[f];
edge[num_edge].to=to;
edge[num_edge].dis=c;
h[f]=num_edge;
}
struct Wt{
int ki;
ll bi;
Wt(){ki=bi=0;}
Wt(int _k,ll _b){ki=_k,bi=_b;}
}pt[N];
int n,q;
int fafa[N],fa_id[N];
namespace wtf{
void main(){
/*orz*/
return ;
}
}
void pre_dfs(int now,int fa){
int v;
int x=pt[now].ki,y=pt[now].bi;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fa)continue;
fafa[v]=now;
fa_id[v]=i;
pt[v]=Wt(-x,edge[i].dis-y);
pre_dfs(v,now);
}
return;
}
namespace qwq{
void main(){
int op,x,y;ll dd;
ll p=edge[2].dis;
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
if(x!=y){
if(dd==p){
puts("inf");
}
else{
puts("none");
}
}
else {
if(x==1){
if(dd%2)puts("none");
else printf("%lld\n",dd/2);
}
if(x==2){
ll tt=2*p-dd;
if(tt%2)puts("none");
else printf("%lld\n",tt/2);
}
}
}
else{
p=y;
}
}
return ;
}
}
namespace task_1{
void main(){
int op,x,y;ll dd;
int kk,bb;
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
kk=pt[x].ki+pt[y].ki;
bb=pt[x].bi+pt[y].bi;
dd=dd-bb;
if(kk==0){
if(dd==0)puts("inf");
else puts("none");
}
else if(dd%abs(kk)!=0)puts("none");
else printf("%lld\n",dd/kk);
}
else {
edge[fa_id[x]].dis=y;
edge[fa_id[x]^1].dis=y;
pre_dfs(fafa[x],fafa[fafa[x]]);
}
}
return ;
}
}
namespace niconiconi{
int dep[N],top[N],son[N],size[N],dfn[N],rnk[N],tot=0;
void print(ll xxx){
if(!xxx)return ;
print(xxx/10);
putchar(xxx%10+'0');
return ;
}
void dfs_1(int now){
int v;size[now]=1;
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fafa[now])continue;
dep[v]=dep[now]+1;
dfs_1(v);
size[now]+=size[v];
if(!son[now]||size[son[now]]<size[v])son[now]=v;
}
return ;
}
void dfs_2(int now,int t){
int v;top[now]=t;
dfn[now]=++tot,rnk[tot]=now;
if(!son[now])return ;
dfs_2(son[now],t);
for(ri i=h[now];i;i=edge[i].ne){
v=edge[i].to;
if(v==fafa[now]||v==son[now])continue;
dfs_2(v,v);
}
return ;
}
struct Tag{
int o;//标记的正负
ll dt;
Tag(){o=dt=0;}
Tag(int o){o=dt=o;}
Tag(int _o,ll _dt){o=_o,dt=_dt;}
Tag operator +(const Tag &b)const{
Tag tmp=*this;
if(tmp.o==0)tmp=b;
else if(b.o==0)return tmp;
else {
if(o!=b.o){
tmp.dt=dt-b.dt;
}
else tmp.dt=dt+b.dt;
}
return tmp;
}
};
Tag tag[N<<2];
void build(int now,int l,int r){
tag[now]=Tag(0);
if(l==r){
return ;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
return ;
}
int L,R;
Tag dta;
inline void pushdown(int now){
if(tag[now].o==0)return ;
tag[now<<1]=tag[now<<1]+tag[now];
tag[now<<1|1]=tag[now<<1|1]+tag[now];
tag[now]=Tag(0);
return ;
}
void update(int now,int l,int r){
if(L<=l&&r<=R){
tag[now]=tag[now]+dta;
return ;
}
int mid=(l+r)>>1;
pushdown(now);
if(L<=mid)update(now<<1,l,mid);
if(mid<R)update(now<<1|1,mid+1,r);
return ;
}
Wt pa,pb;
int t;
void query(int now,int l,int r){
if(l==r){
//int kkk=pt[rnk[l]].ki,bbb=pt[rnk[l]].bi;
if(tag[now].o!=0){
if(tag[now].o!=pt[rnk[l]].ki){
pt[rnk[l]].bi-=tag[now].dt;
}
else{
pt[rnk[l]].bi+=tag[now].dt;
}
tag[now]=Tag(0);
}
//pa.ki=pt[rnk[l]].ki;
//pa.bi=pt[rnk[l]].bi;
return;
}
int mid=(l+r)>>1;
pushdown(now);
if(t<=mid)query(now<<1,l,mid);
else query(now<<1|1,mid+1,r);
return ;
}
void main(){
int op,x,y;
ll kk,bb,dd;
dep[1]=0;
dfs_1(1);
dfs_2(1,1);
build(1,1,n);
while(q--){
read(op),read(x),read(y);
if(op==1){
read(dd);
t=dfn[x];//pa=Wt(0,0);
query(1,1,n);
t=dfn[y];//pb=Wt(pa.ki,pa.bi),pa=Wt(0,0);
query(1,1,n);
//printf("%d %d %d %d\n",pa.ki,pb.ki,pa.bi,pb.bi);
kk=pt[x].ki+pt[y].ki;
bb=pt[x].bi+pt[y].bi;
dd=dd-bb;
if(kk==0){
if(dd==0)puts("inf");
else puts("none");
}
else if(dd%abs(kk)!=0)puts("none");
else {
if(dd==0)puts("0");
else {
dd=dd/kk;
if(dd<0)dd=-dd,putchar('-');
print(dd);
puts("");
}
//printf("%lld\n",dd/kk);
}
}
else {
dd=edge[fa_id[x]].dis;
edge[fa_id[x]].dis=edge[fa_id[x]^1].dis=y;
dd=1ll*y-dd;
//printf("%lld\n",dd);
L=dfn[x],R=dfn[x]+size[x]-1;
//t=dfn[x];pa=Wt(0,0);
//query(1,1,n);
pa=pt[x];
//printf("%d\n",pa.ki);
dta=Tag(pa.ki,dd);
update(1,1,n);
//update_subtree()
}
}
return ;
}
}
int main(){
int x,y;
freopen("equation.in","r",stdin);
freopen("equation.out","w",stdout);
read(n),read(q);
for(ri i=2;i<=n;i++){
read(x),read(y);
add_edge(i,x,y);
add_edge(x,i,y);
}
pt[1].ki=1,pt[1].bi=0;
fafa[1]=0;
pre_dfs(1,0);
if(q==0)wtf::main();
else if(n==2)qwq::main();
else if(n<=2000)task_1::main();
else niconiconi::main();
fclose(stdin);
fclose(stdout);
return 0;
}
[NOIP10.6模拟赛]2.equation题解--DFS序+线段树的更多相关文章
- BZOJ 3252题解(贪心+dfs序+线段树)
题面 传送门 分析 此题做法很多,树形DP,DFS序+线段树,树链剖分都可以做 这里给出DFS序+线段树的代码 我们用线段树维护到根节点路径上节点权值之和的最大值,以及取到最大值的节点编号x 每次从根 ...
- 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树
题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...
- BZOJ1103 [POI2007]大都市meg dfs序 线段树
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1103 题意概括 一棵树上,一开始所有的边权值为1,我们要支持两种操作: 1. 修改某一条边的权值为 ...
- CodeForces 877E Danil and a Part-time Job(dfs序+线段树)
Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...
- 洛谷P3178 [HAOI2015]树上操作(dfs序+线段树)
P3178 [HAOI2015]树上操作 题目链接:https://www.luogu.org/problemnew/show/P3178 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边 ...
- CodeForces 877E DFS序+线段树
CodeForces 877E DFS序+线段树 题意 就是树上有n个点,然后每个点都有一盏灯,给出初始的状态,1表示亮,0表示不亮,然后有两种操作,第一种是get x,表示你需要输出x的子树和x本身 ...
- [51nod 1681]公共祖先(dfs序+线段树合并)
[51nod 1681]公共祖先(dfs序+线段树合并) 题面 给出两棵n(n<=100000)个点的树,对于所有点对求它们在两棵树中公共的公共祖先数量之和. 如图,对于点对(2,4),它们在第 ...
- 牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树)
牛客wannafly 挑战赛14 B 前缀查询(trie树上dfs序+线段树) 链接:https://ac.nowcoder.com/acm/problem/15706 现在需要您来帮忙维护这个名册, ...
- codevs1228 (dfs序+线段树)
1228 苹果树 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结 ...
随机推荐
- win10系统搭建vagrant时开启bios,虚拟化问题
VT-x is disabled in the BIOS的意思是VT-X虚拟化技术处于禁止关闭状态,需要在电脑主板BIOS中开启CPU虚拟化技术thinkpad重启F1进入BIOS,选择: Sercu ...
- SqlServer自动锁定sa解决代码
ALTER LOGIN sa ENABLE ; GO ALTER LOGIN sa WITH PASSWORD = '' unlock, check_policy = off, check_expir ...
- 在基于Android以及Jetson TK平台上如何写32位的Thumb-2指令
由于Android以及Jetson TK的编译工具链中的汇编器仍然不支持大部分的32位Thumb-2指令,比如add.w,因此我们只能通过手工写机器指令码来实现想要的指令.下面我将简单地介绍如何在AR ...
- 使用VLC发送TS流与播放TS流
使用VLC发送TS流与播放TS流 一.如何使用VLC发送TS流 1.添加一个文件至VLC 2.选择串流,继续 3.选择UDP,点击添加 4.输入地址及端口 5.选择h.264+mp3(TS) 6.ne ...
- SQL Server 高级函数汇总【转】
看到一个帖子,博主收集的很全,里面涵盖了一些常用的内置函数,特此收藏下: 原文链接:https://blog.csdn.net/wang1127248268/article/details/53406 ...
- Crontab的格式说明
第1列分钟1-59第2列小时1-23(0表示子夜)第3列日1-31第4列月1-12第5列星期0-6(0表示星期天)第6列要运行的命令 下面是crontab的格式:分 时 日 月 星期 要运行的命令 这 ...
- 简单范例php调用C# WebService
准备工作:1. 安装IIS对于PHP的支持,看这里2. 要调用Web Service需要nusoap支持,只要弄个nusoap.php就可以了,把它放在要运行的php文件能够引用的地方,比如我放在同一 ...
- 解决报错Fatal error in launcher
换电脑重装python,打算安装第三方库的时候出现错误: Fatal error in launcher 然而在网上搜到的大多数是解决 —— Fatal error in launcher: Unab ...
- docker教程(1) - 快速使用
docker 笔记(1) --docker安装.获取镜像.启动容器.删除容器 一.安装 Docker 官方文档 根据官方文档整理简单流程 从Docker Hub下载mac包 运行磁盘镜像,将Docke ...
- 跨域跨域跨域,从此say goodbye
跨域这个问题每个开发者都会遇到,只是时间先后而已,你不搞清楚它他就像狗皮膏药一样粘着你,在你求职生涯中不停的遇到,然后你每次都要做这个功课,终于有一天这个名词已经让我忍无可忍了,下定决心必须搞定它,要 ...