HDU6200 mustedge mustedge mustedge
不用看题就知道这是和什么tarjan、缩点或桥一类有关的题。
谁让他取题目叫一个mustedge还连续写3次的(哦,似乎是因为那个比赛的题目都是这个画风)
必须的边 》必须要经过的边 》 桥。
主要是动态维护问题,幸好只有加边操作。
建dfs树之后,在dfs树上加边其实就是让dfs树上一些边没有用了。(就这一点我想了很久才想到,真是zz啊)
建dfs树之后,在dfs树上加边其实就是让dfs树上一些边没有用了。(就这一点我想了很久才想到,真是zz啊)
那么可以用树剖来维护这个东西(这不是显然的么)
然后你就非常开心地打了一个树剖,一开始树上边的权值都为1,然后每次区间修改(把一个区间所有边的权值变为0)
然后你非常开心地交了。
然后T了。
这道题裸的树剖是不行di。106106专卡你。
需要一些小技巧。
我们知道,我们最多有106106个操作,但是我们也最多只有106106个点,也就是106−1106−1条边。
那么我们在频繁清零的时候,很多边是重复清零了的。
如果我们每个边最多清一次0,就不会很慢。
那就。。。并查集,维护每条边上方第一个没有清零的边(有用的边)。
我们按dfs序建树状数组。然后每次修改就是向上跳到第一个还有没有清零边,然后log时间修改,就是单点修改区间查询。
所以说修改的总时间复杂度不超过nlognnlogn,而裸的树剖还要多带一个loglog(求lca一个,修改一个)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | //Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> #include<vector> using namespace std; const int maxn=1e5+10; int T,n,m,Q; int aa,ff; char cc; int read() { aa=0;cc= getchar ();ff=1; while (cc< '0' ||cc> '9' ) { if (cc== '-' ) ff=-1; cc= getchar (); } while (cc>= '0' &&cc<= '9' ) aa=aa*10+cc- '0' ,cc= getchar (); return aa*ff; } struct Edge{ int u,v; }; vector<Edge> G; int fir[maxn],nxt[2*maxn],to[2*maxn],e=0; void add( int x, int y) { to[++e]=y;nxt[e]=fir[x];fir[x]=e; to[++e]=x;nxt[e]=fir[y];fir[y]=e; } int f[maxn]; int find( int x) { return x==f[x]? x:f[x]=find(f[x]);} int fa[maxn],size[maxn],son[maxn],dep[maxn]; void dfs1( int pos) { size[pos]=1; int y,z; for (y=fir[pos];y;y=nxt[y]) { if ((z=to[y])==fa[pos]) continue ; fa[z]=pos;dep[z]=dep[pos]+1; dfs1(z); size[pos]+=size[z]; if (size[z]>size[son[pos]]) son[pos]=z; } } int id[maxn],top[maxn],cnt; void dfs2( int pos, int tp) { id[pos]=++cnt; top[pos]=tp; if (!son[pos]) return ; dfs2(son[pos],tp); int y,z; for (y=fir[pos];y;y=nxt[y]) { if ((z=to[y])==fa[pos]||to[y]==son[pos]) continue ; dfs2(z,z); } } int sz[maxn]; int q( int l, int r) { int rs=0;l--; while (r) { rs+=sz[r]; r-=(r&-r); } while (l) { rs-=sz[l]; l-=(l&-l); } return rs; } void chge( int l, int r){ int rr; for (r=find(r);r>=l;r=f[r]) { rr=r; while (rr<=n){ sz[rr]--; rr+=(rr&-rr); } f[r]=find(r-1); } } void get_lca( int x, int y, int p) { int rs=0; while (top[x]!=top[y]) { if (dep[top[x]]<dep[top[y]]) swap(x,y); if (p) rs+=q(id[top[x]],id[x]); else chge(id[top[x]],id[x]); x=fa[top[x]]; } if (x!=y) { if (dep[x]<dep[y]) swap(x,y); if (p) rs+=q(id[y]+1,id[x]); else chge(id[y]+1,id[x]); } if (p) printf ( "%d\n" ,rs); } int main() { T=read(); int x,y,z,xx,yy; for ( int qaq=1;qaq<=T;++qaq) { n=read();m=read(); G.clear(); e=0; cnt=0; memset (fir,0, sizeof (fir)); memset (son,0, sizeof (son)); memset (fa,0, sizeof (fa)); for ( int i=1;i<=n;++i) f[i]=i; for ( int i=1;i<=m;++i) { x=read();y=read(); xx=find(x);yy=find(y); if (xx!=yy) add(x,y),f[xx]=yy; else G.push_back(Edge{x,y}); } Q=read(); dep[1]=1; dfs1(1); dfs2(1,1); for ( int i=1;i<=n;++i) f[i]=i; for ( int i=1;i<=n;++i) sz[i]=(i&-i); z=G.size(); for ( int i=0;i<z;++i) get_lca(G[i].u,G[i].v,0); printf ( "Case #%d:\n" ,qaq); for ( int i=1;i<=Q;++i) { x=read();y=read();z=read(); get_lca(y,z,x-1); } } return 0; } |
除此之外,也可以用树状数组维护每个点到根的距离,然后每次修改就是把子树的区间-1,这样就是区间修改单点查询了。
不过还是要用并查集维护每条边上方第一个没有清零的边(有用的边)。
所以说这道题的重点就是避免重复的修改浪费时间,用并查集维护上方第一个没有清零的边。
弱者就是会被欺负呀
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:使用Catalyst进行自然语言处理
· 分享一个我遇到过的“量子力学”级别的BUG。
· Linux系列:如何调试 malloc 的底层源码
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 历时 8 年,我冲上开源榜前 8 了!
· 物流快递公司核心技术能力-海量大数据处理技术
· 四大AI编程工具组合测评
· 关于能否用DeepSeek做危险的事情,DeepSeek本身给出了答案
· 如何在 Github 上获得 1000 star?