题目大意:
  给定一个环长为奇数的带权基环树,支持以下两种操作:
    1.两点间最短路取反;
    2.两点间最短路求最大子段和。
思路:
  首先找出环,然后对每一个外向树轻重链剖分,
  用线段树维护每一个区间的和、前缀和最值、后缀和最值及子段最值。
  每次修改时,分下列两种情况讨论:
    1.两个点在同一棵外向树上,按照普通的树链剖分修改,线段树上打取反的标记。
    2.两个点再不同外向树上,先各自沿着链往上跳,然后都跳到环上的时候,可以将两个点在环中的序号减一减,对环长取模,看看哪个小。
  查询时大致和修改一样,但是合并两个区间的时候很麻烦,要注意考虑完全。
细节:
  1.找环可以在DFS的时候和树剖一起完成,也可以用并查集写,两个效率差不多。
  2.标记可以打好几次,所以不能简单地把标记赋值为true,而是每次取反。
这题代码量比较大,vjudge上除了周骏东都是10K左右。网上的题解也很少,中英文都没有找到。
一开始写挂的地方特别多,对拍对了好多次才排出所有的错。

 #include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
char ch;
bool neg=false;
while(!isdigit(ch=getchar())) if(ch=='-') neg=true;
int x=ch^'';
while(isdigit(ch=getchar())) x=(((x<<)+x)<<)+(ch^'');
return neg?-x:x;
}
const int V=;
struct Edge {
int to,w;
};
std::vector<Edge> e[V];
inline void add_edge(const int u,const int v,const int w) {
e[u].push_back((Edge){v,w});
}
int par[V],size[V],son[V],cyc[V],id[V],w[V],root[V],dep[V],top[V];
int cnt_cyc,cnt;
bool on_cycle[V];
bool dfs1(const int x,const int p) {
if(par[x]||(x==&&p!=)) {
on_cycle[x]=true;
return true;
}
size[x]=;
par[x]=p;
bool flag=false;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i].to;
if(y==p||on_cycle[y]) continue;
if(dfs1(y,x)) {
par[x]=;
id[y]=++cnt;
w[cnt]=e[x][i].w;
if(on_cycle[x]) {
flag=true;
} else {
on_cycle[x]=true;
}
} else {
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
if(on_cycle[x]) cyc[++cnt_cyc]=x;
return on_cycle[x]^flag;
}
int rt;
void dfs2(const int x) {
dep[x]=dep[par[x]]+;
top[x]=x==son[par[x]]?top[par[x]]:x;
root[x]=cyc[rt];
if(son[x]) {
id[son[x]]=++cnt;
dfs2(son[x]);
}
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i].to;
if(y==par[x]||on_cycle[y]) continue;
if(y==son[x]) {
w[id[y]]=e[x][i].w;
continue;
}
id[y]=++cnt;
w[cnt]=e[x][i].w;
dfs2(y);
}
}
void dfs3(const int x,const int p) {
par[x]=p;
son[x]=;
size[x]=;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i].to;
if(y==p||on_cycle[y]) continue;
dfs3(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
class SegmentTree {
private:
int left[V<<],right[V<<];
long long sum[V<<],submax[V<<],submin[V<<],premax[V<<],premin[V<<],sufmax[V<<],sufmin[V<<];
bool neg[V<<];
int sz,newnode() {
return ++sz;
}
void negate(long long &x) {
x=-x;
}
void reverse(const int p) {
negate(sum[p]);
negate(submax[p]);
negate(submin[p]);
std::swap(submax[p],submin[p]);
negate(premax[p]);
negate(premin[p]);
std::swap(premax[p],premin[p]);
negate(sufmax[p]);
negate(sufmin[p]);
std::swap(sufmax[p],sufmin[p]);
}
void push_up(const int p) {
sum[p]=sum[left[p]]+sum[right[p]];
submax[p]=std::max(std::max(submax[left[p]],submax[right[p]]),sufmax[left[p]]+premax[right[p]]);
submin[p]=std::min(std::min(submin[left[p]],submin[right[p]]),sufmin[left[p]]+premin[right[p]]);
premax[p]=std::max(premax[left[p]],sum[left[p]]+premax[right[p]]);
premin[p]=std::min(premin[left[p]],sum[left[p]]+premin[right[p]]);
sufmax[p]=std::max(sufmax[right[p]],sum[right[p]]+sufmax[left[p]]);
sufmin[p]=std::min(sufmin[right[p]],sum[right[p]]+sufmin[left[p]]);
}
void push_down(const int p) {
if(!neg[p]) return;
reverse(left[p]);
reverse(right[p]);
neg[p]=false;
neg[left[p]]=!neg[left[p]];
neg[right[p]]=!neg[right[p]];
}
public:
struct Ans {
long long sum,pre,sub,suf;
};
int root;
void build(int &p,const int b,const int e) {
p=newnode();
if(b==e) {
sum[p]=w[b];
submax[p]=premax[p]=sufmax[p]=std::max(w[b],);
submin[p]=premin[p]=sufmin[p]=std::min(w[b],);
return;
}
int mid=(b+e)>>;
build(left[p],b,mid);
build(right[p],mid+,e);
push_up(p);
}
void modify(const int p,const int b,const int e,const int l,const int r) {
if((b==l)&&(e==r)) {
reverse(p);
neg[p]=!neg[p];
return;
}
push_down(p);
int mid=(b+e)>>;
if(l<=mid) modify(left[p],b,mid,l,std::min(mid,r));
if(r>mid) modify(right[p],mid+,e,std::max(mid+,l),r);
push_up(p);
}
Ans query(const int p,const int b,const int e,const int l,const int r) {
if((b==l)&&(e==r)) {
return (Ans){sum[p],premax[p],submax[p],sufmax[p]};
}
push_down(p);
int mid=(b+e)>>;
long long leftsum=,leftpre=,leftsub=,leftsuf=,rightsum=,rightpre=,rightsub=,rightsuf=;
if(l<=mid) {
Ans tmp=query(left[p],b,mid,l,std::min(mid,r));
leftsum=tmp.sum;
leftpre=tmp.pre;
leftsub=tmp.sub;
leftsuf=tmp.suf;
}
if(r>mid) {
Ans tmp=query(right[p],mid+,e,std::max(mid+,l),r);
rightsum=tmp.sum;
rightpre=tmp.pre;
rightsub=tmp.sub;
rightsuf=tmp.suf;
}
long long sum,pre,sub,suf;
sum=leftsum+rightsum;
pre=std::max(leftpre,leftsum+rightpre);
sub=std::max(std::max(leftsub,rightsub),leftsuf+rightpre);
suf=std::max(rightsuf,rightsum+leftsuf);
return (Ans){sum,pre,sub,suf};
}
};
SegmentTree t;
int n;
inline void modify(int x,int y) {
if(root[x]!=root[y]) {
while(top[x]!=root[x]) {
t.modify(t.root,,n,id[top[x]],id[x]);
x=par[top[x]];
}
if(x!=top[x]) {
t.modify(t.root,,n,id[son[top[x]]],id[x]);
x=top[x];
}
while(top[y]!=root[y]) {
t.modify(t.root,,n,id[top[y]],id[y]);
y=par[top[y]];
}
if(y!=top[y]) {
t.modify(t.root,,n,id[son[top[y]]],id[y]);
y=top[y];
}
} else {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
t.modify(t.root,,n,id[top[x]],id[x]);
x=par[top[x]];
}
if(x!=y) {
if(dep[x]<dep[y]) std::swap(x,y);
t.modify(t.root,,n,id[son[y]],id[x]);
}
return;
}
if((id[y]-id[x]+cnt_cyc)%cnt_cyc>(id[x]-id[y]+cnt_cyc)%cnt_cyc) std::swap(x,y);
if(id[x]<id[y]) {
t.modify(t.root,,n,id[x],id[y]-);
} else {
t.modify(t.root,,n,id[x],cnt_cyc);
if(id[y]!=) t.modify(t.root,,n,,id[y]-);
}
}
inline long long query(int x,int y) {
long long lastsum=,lastpre=,lastsub=,lastsuf=,nextsum=,nextpre=,nextsub=,nextsuf=;
long long ans=;
if(root[x]!=root[y]) {
while(top[x]!=root[x]) {
SegmentTree::Ans tmp=t.query(t.root,,n,id[top[x]],id[x]);
lastsub=std::max(std::max(lastsub,tmp.sub),lastpre+tmp.suf);
lastpre=std::max(tmp.pre,lastpre+tmp.sum);
lastsuf=std::max(lastsuf,lastsum+tmp.suf);
lastsum+=tmp.sum;
ans=std::max(ans,lastsub);
x=par[top[x]];
}
if(x!=top[x]) {
SegmentTree::Ans tmp=t.query(t.root,,n,id[son[top[x]]],id[x]);
lastsub=std::max(std::max(lastsub,tmp.sub),lastpre+tmp.suf);
lastpre=std::max(tmp.pre,lastpre+tmp.sum);
lastsuf=std::max(lastsuf,lastsum+tmp.suf);
lastsum+=tmp.sum;
ans=std::max(ans,lastsub);
x=top[x];
}
while(top[y]!=root[y]) {
SegmentTree::Ans tmp=t.query(t.root,,n,id[top[y]],id[y]);
nextsub=std::max(std::max(nextsub,tmp.sub),nextpre+tmp.suf);
nextpre=std::max(tmp.pre,nextpre+tmp.sum);
nextsuf=std::max(nextsuf,nextsum+tmp.suf);
nextsum+=tmp.sum;
ans=std::max(ans,nextsub);
y=par[top[y]];
}
if(y!=top[y]) {
SegmentTree::Ans tmp=t.query(t.root,,n,id[son[top[y]]],id[y]);
nextsub=std::max(std::max(nextsub,tmp.sub),nextpre+tmp.suf);
nextpre=std::max(tmp.pre,nextpre+tmp.sum);
nextsuf=std::max(nextsuf,nextsum+tmp.suf);
nextsum+=tmp.sum;
ans=std::max(ans,nextsub);
y=top[y];
}
} else {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) {
std::swap(x,y);
std::swap(lastsum,nextsum);
std::swap(lastsub,nextsub);
std::swap(lastpre,nextpre);
std::swap(lastsuf,nextsuf);
}
SegmentTree::Ans tmp=t.query(t.root,,n,id[top[x]],id[x]);
lastsub=std::max(std::max(lastsub,tmp.sub),lastpre+tmp.suf);
lastpre=std::max(tmp.pre,lastpre+tmp.sum);
lastsuf=std::max(lastsuf,lastsum+tmp.suf);
lastsum+=tmp.sum;
ans=std::max(ans,lastsub);
x=par[top[x]];
}
if(x!=y) {
if(dep[x]<dep[y]) {
std::swap(x,y);
std::swap(lastsum,nextsum);
std::swap(lastsub,nextsub);
std::swap(lastpre,nextpre);
std::swap(lastsuf,nextsuf);
}
SegmentTree::Ans tmp=t.query(t.root,,n,id[son[y]],id[x]);
lastsub=std::max(std::max(lastsub,tmp.sub),lastpre+tmp.suf);
lastpre=std::max(tmp.pre,lastpre+tmp.sum);
lastsuf=std::max(lastsuf,lastsum+tmp.suf);
lastsum+=tmp.sum;
}
ans=std::max(ans,std::max(lastsub,lastpre+nextpre));
return ans;
}
if((id[y]-id[x]+cnt_cyc)%cnt_cyc>(id[x]-id[y]+cnt_cyc)%cnt_cyc) {
std::swap(x,y);
std::swap(lastsum,nextsum);
std::swap(lastsub,nextsub);
std::swap(lastpre,nextpre);
std::swap(lastsuf,nextsuf);
}
SegmentTree::Ans tmp;
if(id[x]<id[y]) {
tmp=t.query(t.root,,n,id[x],id[y]-);
} else {
SegmentTree::Ans tmp1,tmp2;
tmp1=t.query(t.root,,n,id[x],cnt_cyc);
if(id[y]!=) tmp2=t.query(t.root,,n,,id[y]-);
if(id[y]!=) {
tmp.sum=tmp1.sum+tmp2.sum;
tmp.sub=std::max(std::max(tmp1.sub,tmp2.sub),tmp1.suf+tmp2.pre);
tmp.pre=std::max(tmp1.pre,tmp1.sum+tmp2.pre);
tmp.suf=std::max(tmp2.suf,tmp2.sum+tmp1.suf);
} else {
tmp=tmp1;
}
}
ans=std::max(ans,tmp.sub);
ans=std::max(ans,lastpre+tmp.pre);
ans=std::max(ans,nextpre+tmp.suf);
ans=std::max(ans,lastpre+nextpre+tmp.sum);
return ans;
}
int main() {
n=getint();
for(int i=;i<=n;i++) {
int u=getint(),v=getint(),w=getint();
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs1(,);
for(rt=;rt<=cnt_cyc;rt++) {
if(rt==cnt_cyc) dfs3(cyc[rt],);
dfs2(cyc[rt]);
}
t.build(t.root,,n);
for(int m=getint();m;m--) {
char op[];
scanf("%1s",op);
switch(op[]) {
case 'f': {
int x=getint(),y=getint();
modify(x,y);
break;
}
case '?': {
int x=getint(),y=getint();
printf("%lld\n",query(x,y));
break;
}
}
}
return ;
}

附数据生成器:

 #include<ctime>
#include<vector>
#include<cstdio>
#include<cstdlib>
const int V=;
std::vector<int> e[V];
inline void add_edge(const int u,const int v) {
e[u].push_back(v);
}
int par[V],dep[V],top[V],son[V],size[V];
void dfs1(const int x) {
size[x]=;
dep[x]=dep[par[x]]+;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i];
dfs1(y);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
void dfs2(const int x) {
top[x]=x==son[par[x]]?top[par[x]]:x;
for(unsigned i=;i<e[x].size();i++) {
int &y=e[x][i];
dfs2(y);
}
}
inline int get_lca(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
x=par[top[x]];
}
if(dep[x]<dep[y]) std::swap(x,y);
return y;
}
int main() {
srand(time(NULL));
int n=,m=,w=;
printf("%d\n",n);
for(int i=;i<=n;i++) {
printf("%d %d %d\n",par[i]=rand()%(i-)+,i,rand()%w-w/);
add_edge(par[i],i);
}
dfs1();
dfs2();
for(int i=;i<n;i++) {
if(i!=par[n]) {
int lca=get_lca(i,n);
if((dep[i]+dep[n]-dep[lca]*)&) continue;
printf("%d %d %d\n",i,n,rand()%w-w/);
break;
}
}
printf("%d\n",m);
for(int i=;i<=m;i++) {
int op=rand()%;
if(op) {
int y=rand()%(n-)+;
int x=rand()%(y-)+;
printf("? %d %d\n",x,y);
} else {
int y=rand()%(n-)+;
int x=rand()%(y-)+;
printf("f %d %d\n",x,y);
}
}
return ;
}

[CodeChef-QTREE]Queries on tree again!的更多相关文章

  1. BZOJ 3221: [Codechef FEB13] Obserbing the tree树上询问( 可持久化线段树 + 树链剖分 )

    树链剖分+可持久化线段树....这个一眼可以看出来, 因为可持久化所以写了标记永久化(否则就是区间修改的线段树的持久化..不会), 结果就写挂了, T得飞起...和管理员拿数据调后才发现= = 做法: ...

  2. codechef Prime Distance On Tree(树分治+FFT)

    题目链接:http://www.codechef.com/problems/PRIMEDST/ 题意:给出一棵树,边长度都是1.每次任意取出两个点(u,v),他们之间的长度为素数的概率为多大? 树分治 ...

  3. Codechef:Path Triples On Tree

    Path Triples On Tree 题意是求树上都不相交或者都相交的路径三元组数量. 发现blog里没什么树形dp题,也没有cc题,所以来丢一道cc上的树形dp题. 比较暴力,比较恶心 #inc ...

  4. [BZOJ 3221][Codechef FEB13] Obserbing the tree树上询问

    [BZOJ 3221]Obserbing the tree树上询问 题目 小N最近在做关于树的题.今天她想了这样一道题,给定一棵N个节点的树,节点按1~N编号,一开始每个节点上的权值都是0,接下来有M ...

  5. Codechef TSUM2 Sum on Tree 点分治、李超线段树

    传送门 点分治模板题都不会迟早要完 发现这道题需要统计所有路径的信息,考虑点分治统计路径信息. 点分治之后,因为路径是有向的,所以对于每一条路径都有向上和向下的两种.那么如果一条向上的路径,点数为\( ...

  6. CodeChef Gcd Queries

    Gcd Queries   Problem code: GCDQ   Submit All Submissions   All submissions for this problem are ava ...

  7. 【CodeChef】QTREE- Queries on tree again!

    题解 给你一棵基环树,环长为奇数(两点间最短路径只有一条) 维护两点间路径最大子段和,支持把一条路径上的值取反 显然只要断开一条边维护树上的值,然后对于那条边分类讨论就好了 维护树上的值可以通过树链剖 ...

  8. codechef T6 Pishty and tree dfs序+线段树

    PSHTTR: Pishty 和城堡题目描述 Pishty 是生活在胡斯特市的一个小男孩.胡斯特是胡克兰境内的一个古城,以其中世纪风格 的古堡和非常聪明的熊闻名全国. 胡斯特的镇城之宝是就是这么一座古 ...

  9. Codechef Prime Distance On Tree

    [传送门] FFT第四题! 暑假的时候只会点分,然后合并是暴力合并的...水过去了... 其实两条路径长度的合并就是卷积的过程嘛,每次统计完路径就自卷积一下. 刚开始卷积固定了值域.T了.然后就不偷懒 ...

随机推荐

  1. Tickets HDU1260

    题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=1260 (http://www.fjutacm.com/Problem.jsp?pid=1382) 题意 ...

  2. springboot中报异常Whitelabel Error Page

    开始以为是url写错了,但其实不是,然后启动application类在的包是要在最顶部,并且和pom中groupid一样 这个也没错,后来发现能访问RestController中的url,但是进不了方 ...

  3. ubuntu 18.04 安装 flash

    下载源码包, 解压 sudo cp Downloads/flash_player_npapi_linux.x86_64/libflashplayer.so /usr/lib/mozilla/plugi ...

  4. 创建spring boot项目

    一.创建项目 1.输入https://start.spring.io/ 2.填写group.artifact 3.选择依赖的jar 4.点击创建项目 二.导入项目 1.eclipse的package ...

  5. MyEclipse中Source not found的问题

    1.问题描述 在MyEclipse中想查看源码,结果显示:Source not found ......(大概的意思就是找不到源码包) 2.解决方案 下载相应版本的apache-tomcat-8.5. ...

  6. 第一个Django项目:HelloWorld

    OS:Windows家庭中文版, Python:3.6.3,Django:2.0.3 在前一篇文章中,Django已经顺利安装到了Python中,下面,开发第一个Python项目吧! 1.django ...

  7. python基础--re模块

    常用正则表达式符号 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r& ...

  8. 关于 poScreenCenter 与 poDesktopCenter

    主要是窗体水平方向与垂直方向的的 居中问题,由于水平方向 没有什么,所以不探讨.而垂直方向由于底部有个工具栏,工具栏自身有个高度,所以垂直方向的居中问题,需要探讨下. 结论: poScreenCent ...

  9. 解决Url带中文参数乱码问题

    这里我来介绍下如何配置Tomcat 来解决Url带中文参数乱码问题: 首先打开Tomcat安装目录,以Tomcat7为例,其他版本基本一样: 打开conf文件 打开server.xml 大概在70行左 ...

  10. (一)问候 Log4j 你好

    第一节: Log4j 简介 Log4j -------- log for java(java的日志) 是java主流的日志框架,提供各种类型,各种存储,各种格式,多样化的日志服务: 在爬虫领域,主要用 ...