https://codeforces.com/contest/487/problem/E

http://uoj.ac/problem/30

显然割点走过去就走不回来了...可以看出题目跟点双有关

有一个结论:如果在点双中有不同的点a,b,c,那么一定存在点不重复的路径从a到c再到b。

证明(摘自https://codeforces.com/blog/entry/14832):

显然这样的点双中,点数>=3,因此移除任意点或任意边,仍然连通

新建一张网络流图。用边(u,v,w)表示从u到v容量为w的边

对于原点双中每一条边(u,v),建新边(u,v,1),(v,u,1)

新建点S,T,建边(S,c,2),(a,T,1),(b,T,1)

用拆点的方法,给S,T,c外的每一个点1的容量(拆点过程略)

此时,显然如果S到T的最大流为2,那么存在合法方案

最大流=最小割。显然最小割<=2(割掉(a,T)与(b,T)的边)。割掉新图中任意一条权值为1的边(除(a,T),(b,T)),对应删除原点双的一条边或一个点,显然删除之后原图仍然连通,则新图也连通。因此最小割>1。所以最小割=2

就是说,如果走的时候要穿过一个点双(任意点开始,任意点结束),那么一定可以找到一条合法路径经过点双内权值最小的点

有一个想法:

可以点双缩点成树(圆方树)(每个点双新建一个点,向点双中所有点连边,删除原图中所有边)

(树中代表一个点双的点的权值是点双中点权的最小值)

然后查询就是树上两点路径上最小值,修改就是单点修改(每个点双用一个multiset维护点双中所有点的权值。修改非割点就直接在它属于的点双的multiset中改,如果multiset中最小值变了就对应修改维护最小值的数据结构;修改割点方法在后面),用树剖维护即可

好像还不怎么对。。割点也是属于点双的,但是修改割点权值显然不能暴力修改其属于的所有点双

看了题解,方法是:每个割点就当做属于它在缩点树中的父亲(这个父亲一定代表一个点双;如果不存在父亲就不用维护),每次修改就在这个父亲的multiset那里改;同时每次查询的时候,如果两点的lca代表一个点双,那么除两点路径上最小值外还要考虑lca的父亲(一定是个割点;如果不存在父亲就不用额外查)

然而真的码不动。。5K代码,续了一下午A掉

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<set>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
struct E
{
int to,nxt;
};
int a[];
int n,m,qq;
namespace S
{
#define lc (num<<1)
#define rc (num<<1|1)
int d[];
void setx(int L,int x,int l,int r,int num)
{
if(l==r) {d[num]=x;return;}
int mid=l+((r-l)>>);
if(L<=mid) setx(L,x,l,mid,lc);
else setx(L,x,mid+,r,rc);
d[num]=min(d[lc],d[rc]);
}
int gmin(int L,int R,int l,int r,int num)
{
if(L<=l&&r<=R) return d[num];
int mid=l+((r-l)>>);int ans=0x3f3f3f3f;
if(L<=mid) ans=min(ans,gmin(L,R,l,mid,lc));
if(mid<R) ans=min(ans,gmin(L,R,mid+,r,rc));
return ans;
}
int getmin(int L,int R,int l,int r,int num)
{
if(L>R) swap(L,R);
//printf("19t%d %d %d\n",L,R,gmin(L,R,l,r,num));
return gmin(L,R,l,r,num);
}
}
int cnt;
int bno[];
bool iscut[];
namespace T
{
E e[];
int f1[],ne;
void me(int x,int y)
{
e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;
e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;
}
bool vis[];
int sz[],hson[],tp[],dep[];
void dfs1(int u,int fa)
{
sz[u]=;
vis[u]=;
for(int k=f1[u];k;k=e[k].nxt)
if(e[k].to!=fa)
{
dep[e[k].to]=dep[u]+;
dfs1(e[k].to,u);
sz[u]+=sz[e[k].to];
if(sz[e[k].to]>sz[hson[u]])
hson[u]=e[k].to;
}
}
int ar[],lp[],f[];
void dfs2(int u,int fa)
{
ar[++ar[]]=u;lp[u]=ar[];
f[u]=fa;
if(u==hson[fa]) tp[u]=tp[fa];
else tp[u]=u;
if(hson[u]) dfs2(hson[u],u);
for(int k=f1[u];k;k=e[k].nxt)
if(e[k].to!=fa&&e[k].to!=hson[u])
dfs2(e[k].to,u);
}
multiset<int> s[];//s[i]维护i号点双里面所有非割点的权值
void update2(int x)
{
a[x+n]=*s[x].begin();
//printf("9t%d %d\n",x,a[x+n]);
S::setx(lp[x+n],a[x+n],,n+cnt,);
}
void change(int x,int y)
{
if(!iscut[x])
{
s[bno[x]].erase(s[bno[x]].find(a[x]));
s[bno[x]].insert(a[x]=y);
update2(bno[x]);
}
else
{
if(f[x])
{
s[f[x]-n].erase(s[f[x]-n].find(a[x]));
s[f[x]-n].insert(y);
update2(f[x]-n);
}
a[x]=y;
S::setx(lp[x],y,,n+cnt,);
}
}
int query(int x,int y)
{
if(x==y) return a[x];
//printf("99t%d %d\n",x,y);
//if(!iscut[x]) x=n+bno[x];
//if(!iscut[y]) y=n+bno[y];
int ans=0x3f3f3f3f;
while(tp[x]!=tp[y])
{
if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
//printf("7t%d %d\n",x,y);
ans=min(ans,S::getmin(lp[x],lp[tp[x]],,n+cnt,));
x=f[tp[x]];
}
ans=min(ans,S::getmin(lp[x],lp[y],,n+cnt,));
if(dep[x]>dep[y]) swap(x,y);
if(x>n&&f[x]) ans=min(ans,a[f[x]]);
return ans;
}
void work()
{
int i;
/*
for(i=1;i<=n+cnt;i++)
for(int k=f1[i];k;k=e[k].nxt)
printf("1t%d %d\n",i,e[k].to);
for(i=1;i<=n;i++)
printf("%d ",bno[i]);
puts("t2");
for(i=1;i<=n;i++)
printf("%d ",int(iscut[i]));
puts("t3");
*/
for(i=;i<=n+cnt;i++)
if(!vis[i])
{
dfs1(i,);
dfs2(i,);
}
/*
for(i=1;i<=ar[0];i++)
printf("%d ",ar[i]);
puts("t4");
for(i=1;i<=n+cnt;i++)
printf("%d ",lp[i]);
puts("t5");
for(i=1;i<=n+cnt;i++)
printf("6t%d %d\n",f[i],tp[i]);
*/
for(i=;i<=n;i++)
if(!iscut[i])
{
s[bno[i]].insert(a[i]);
S::setx(lp[i],0x3f3f3f3f,,n+cnt,);
}
else
{
if(f[i])
{
s[f[i]-n].insert(a[i]);
}
S::setx(lp[i],a[i],,n+cnt,);
}
for(i=;i<=cnt;i++)
update2(i);
//for(i=1;i<=ar[0];i++)
// printf("16t%d\n",S::getmin(i,i,1,n+cnt,1));
} }
namespace G
{
E e[];
int f1[],ne=;
void me(int x,int y)
{
e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;
e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;
}
int dfc,dfn[];
pii st[];int tp;
int dfs(int u,int last)
{
//printf("3t%d %d\n",u,last);
int k,v,lowu=dfn[u]=++dfc,chi=,lowv;pii x;
for(k=f1[u];k;k=e[k].nxt)
{
v=e[k].to;
if(!dfn[v])
{
st[++tp]=mp(u,v);++chi;
lowv=dfs(v,k);lowu=min(lowu,lowv);
if(lowv>=dfn[u])
{
//printf("4t%d %d\n",u,v);
iscut[u]=;
++cnt;
for(;;)
{
x=st[tp--];
if(bno[x.fi]!=cnt)
{
bno[x.fi]=cnt;
T::me(n+cnt,x.fi);
}
if(bno[x.se]!=cnt)
{
bno[x.se]=cnt;
T::me(n+cnt,x.se);
}
if(x.fi==u&&x.se==v) break;
}
}
}
else if(dfn[v]<dfn[u]&&k!=(last^))
{
st[++tp]=mp(u,v);
lowu=min(lowu,dfn[v]);
}
}
if(!last&&chi==) iscut[u]=;
//printf("5t%d %d %d\n",u,lowu,dfn[u]);
return lowu;
}
} int main()
{
char tmp[];
int i,x,y;
scanf("%d%d%d",&n,&m,&qq);
for(i=;i<=n;i++) scanf("%d",&a[i]);
for(i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
G::me(x,y);
}
for(i=;i<=n;i++)
if(!G::dfn[i])
G::dfs(i,);
T::work();
while(qq--)
{
scanf("%s%d%d",tmp,&x,&y);
if(tmp[]=='C')
{
T::change(x,y);
}
else
{
printf("%d\n",T::query(x,y));
}
}
return ;
}

Tourists Codeforces - 487E的更多相关文章

  1. Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]

    洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...

  2. codeforces 487E Tourists

    如果不是uoj上有的话(听说这是China Round),我有可能就错过这道题目了(这是我有史以来为oi写的最长的代码,用了我一天TAT!). 题目 传送门. 一个连通无向图,点上有权,支持两种操作: ...

  3. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

  4. CodeForces 487E Tourists(圆方树+线段树+树链剖分)

    题意 ​ \(n\) 个点 \(m\) 条边的无向连通图,每个点有点权,\(q\) 个要求,每次更新一个点的点权或查询两点间路径权值最小的点最小的路径. 思路 ​ 算是圆方树的板子吧?圆方树处理的主要 ...

  5. [UOJ30/Codeforces Round #278 E]Tourists

    传送门 好毒瘤的一道题QAQ,搞了好几好几天. UOJ上卡在了53个点,CF上过了,懒得优化常数了 刚看时一眼Tarjan搞个强连通分量然后缩点树链剖分xjb搞搞就行了,然后写完了,然后WA了QAQ. ...

  6. Solution -「CF 487E」Tourists

    \(\mathcal{Description}\)   Link.   维护一个 \(n\) 个点 \(m\) 条边的简单无向连通图,点有点权.\(q\) 次操作: 修改单点点权. 询问两点所有可能路 ...

  7. Codeforces Round #278 (Div. 2)

    题目链接:http://codeforces.com/contest/488 A. Giga Tower Giga Tower is the tallest and deepest building ...

  8. Educational Codeforces Round 21 Problem E(Codeforces 808E) - 动态规划 - 贪心

    After several latest reforms many tourists are planning to visit Berland, and Berland people underst ...

  9. Codeforces Beta Round #3 B. Lorry 暴力 二分

    B. Lorry 题目连接: http://www.codeforces.com/contest/3/problem/B Description A group of tourists is goin ...

随机推荐

  1. CSU - 1529 Equator —— DP 最大连续和子序列

    题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1529 题解: 一个加强版的最大连续和子序列,序列可以从末尾元素转到首元素. 分两种情 ...

  2. jenkins页面不刷新,设置tomcat缓存

    装jenkins的时候,部署后,访问jenkins页面,输入管理员密码后,出现jenkins页面停滞,看后台catlina日志,发现需要增加tomcat容器的cache,才能加载一些jar包,下面是设 ...

  3. 扩散(diffusion)和弥散(dispersion)有什么区别

    作者:谢易正链接:https://www.zhihu.com/question/23914350/answer/177359196来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  4. Chapter 3 Shared Assemblies and Strongly Named Assemblies

    As time marches on,MS developers and control developer modify their code:they fix bugs,patch securit ...

  5. 让tomcat启动时,自动加载你的项目

    在tomcat-->conf-->serve.xml文件最后加上 <Context path="/atest" docBase="E:\Workspac ...

  6. 静态路由配置及RIP配置实验

    [实验环境] Packet Trace 5.3 模拟软件. [实验步骤] 1.首先要进行IP地址规划.(例如下图格式) 网络名 网络地址 子网掩码 网关 主机IP vlan1 10.10.1.0 25 ...

  7. HihoCoder1470 : 公平的游戏

    描述 有一些人在玩一个游戏.游戏的舞台发生在一个 n 个点的树上. 这个游戏分为很多轮,每一轮都有一些玩家参与,每个玩家都会降落在一条给定的边上(不同玩家的边不同).之后这 n 个点上都会随机出现一个 ...

  8. [ZJOI 2010] 排列计数

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2111 [算法] 一种比较好的理解方式是将该序列看成是一棵堆式存储的二叉树 那么问题转 ...

  9. Day02:数据类型、字符编码、文件处理

    一.基础数据类型与其内置方法 1. int基本使用:int(3.1) 用途:记录年龄,职级等 只能将包含纯数字的字符串类型转换成整形 特点:1.存一个值. 2.不可变 2.float基本使用 floa ...

  10. linux进程状态D和Z的处理

    长期生活在 Linux 环境里,渐渐地就有一种环保意识油然而生.比如,我们会在登录提示里写上"悟空,我跟你说过叫你不要乱扔东西,乱扔东西是不对的.哎呀我话没说完你怎么把 棍子扔掉了?月光宝盒 ...