题目大意:
  给定一个环长为奇数的带权基环树,支持以下两种操作:
    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. Spring4笔记6--Spring与AOP

    Spring与AOP: AOP的引入: 主业务经常需要调用系统级业务(交叉业务),如果在主业务代码中大量的调用系统级业务代码,会使系统级业务与主业务深度耦合在一起,大大影响了主业务逻辑的可读性,降低了 ...

  2. Linux机器如何在公司内网配置代理

    一.通过上网认证 必须在图形界面下使用浏览器(如Firefox)完成上网认证过程. 请先确保本机已经可以正常访问公司内部网络. Firefox上配置代理: 1)打开Firefox首选项,[高级]-[网 ...

  3. c# 通过Windows服务启动外部程序

    1. 新建一个Windows服务应用程序 创建项目——>Visual C# 左侧的"+"——>Windows ——>Windows 服务(右侧模板)——>输 ...

  4. C#之WinForm基础 新建一个不可编辑的comboBox

    慈心积善融学习,技术愿为有情学.善心速造多好事,前人栽树后乘凉.我今于此写经验,愿见文者得启发. 1.拉控件 2.添加可选数据 3.改变基本样式 4.效果图 C#优秀,值得学习.Winform,WPF ...

  5. 利用JS验证查询参数-选择月份后必须选择年份

    js代码: function queryAgentInfo(){ // 标记 var flag=false; //遍历所有月份 $(".month").each(function( ...

  6. 练习题 --- 写出5种css定位语法

    写出至少5种css语法(每种语法不一样)

  7. Java @SuppressWarnings

    @SuppressWarnings() 注解以@开头可以接受参数 @SuppressWarnings("unchecked") 不受检查的警告信息应该被抑制 //: holding ...

  8. Centos之文件搜索命令locate

    locate命令 locate 文件名 在后台数据库中按文件名搜索,搜索速度更快 /var/lib/mlocate #locate命令所搜索的后台数据库 updatedb 更新数据库 locate搜索 ...

  9. 利用sql server直接创建日历

    看到网上有高手直接用sql查询创建日历,也想自己动手实践一遍.笔者这里的实现和网上的都没有什么区别,思路也没有什么新意.觉得好玩,就把它记下来吧. 一.准备知识1.sql的with关键字关于with和 ...

  10. 课堂实验-String类和Arrays类

    课堂实验 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt split Arrays类 sort binarySea ...