1.P6781 [Ynoi2008] rupq2.SNOI 2020 排列 题解3.ICPC WF 2022 2023 Bridging the Gap 过桥4.2023 ICPC Seoul Regional A. Apricot Seeds(Pjudge【NOIP Round #7】冒泡排序)5.CCPC Final 2023 B. Periodic Sequence6.OCPC2024Day1/3rd ucup stage3 Formal Fring7.[PKUSC 2023 D1T3] 天气预测8.[PKUWC 2025 D2T1]网友小 Z 的树9.[PKUWC2025 D2T2]盒子
10.[集训队互测2024]建设终末树
https://qoj.ac/contest/1437/problem/7869
鬼知道官方题解怎么实现得这么 SB 的。
最开始的转化非常神秘:对树复制 M 份,对第 i 份,表示第 i 个物品,以第 i 个物品所挂的点,为根,把边定向成内向树。
应该是这么想出来的:对于链定义 N*M 个布尔变量 f(i,j) 表示第 i 个物品是否在 j 以前。这里用边的定向表示以前/以后。
然后对于必须在连通块内的限制,是简单的,把连通块外围的边强制指向中心。
然后对于给出的 Q 个限制,不难发现是对于指定的若干个物品,它们对应的若干棵树,一些边的方向恒定。这个直接上并查集,就是在 2SAT 中共享一个点。均摊理论上能 O(NM)。
还有内向树的限制,即出度不超过 1,思路就是一边出,其他边必须入,容易用前后缀优化。
所以哪来的树剖和线段树?
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<tuple> #define fi first #define se second #define mkp std::make_pair using ll=long long; using std::min; using std::max; template<class T> void cmax(T&a,T b){a=max(a,b);} template<class T> void cmin(T&a,T b){a=min(a,b);} namespace io { const int __SIZE = (1 << 21) + 1; char ibuf[__SIZE], *iS, *iT, obuf[__SIZE], *oS = obuf, *oT = oS + __SIZE - 1, __c, qu[55]; int __f, qr, _eof; #define Gc() (char)(iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, __SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++) inline void flush () { fwrite (obuf, 1, oS - obuf, stdout), oS = obuf; } inline void gc (char &x) { x = Gc(); } inline void pc (char x) { *oS ++ = x; if (oS == oT) flush (); } inline void pstr (const char *s) { int __len = strlen(s); for (__f = 0; __f < __len; ++__f) pc (s[__f]); } inline void gstr (char *s) { for(__c = Gc(); __c < 32 || __c > 126 || __c == ' ';) __c = Gc(); for(; __c > 31 && __c < 127 && __c != ' ' && __c != '\n' && __c != '\r'; ++s, __c = Gc()) *s = __c; *s = 0; } template <class I> inline bool gi (I &x) { _eof = 0; for (__f = 1, __c = Gc(); (__c < '0' || __c > '9') && !_eof; __c = Gc()) { if (__c == '-') __f = -1; _eof |= __c == EOF; } for (x = 0; __c <= '9' && __c >= '0' && !_eof; __c = Gc()) x = x * 10 + (__c & 15), _eof |= __c == EOF; x *= __f; return !_eof; } template <class I> inline void print (I x) { if (!x) pc ('0'); if (x < 0) pc ('-'), x = -x; while (x) qu[++ qr] = x % 10 + '0', x /= 10; while (qr) pc (qu[qr --]); } struct Flusher_ {~Flusher_(){flush();}}io_flusher_; } const int NV=2e3; namespace sat{ const int M=1.6e7+5,E=2.4e7+5; struct EDGE{ int t,n; } G[E]; int ecnt=2,hd[M]; int dfc,dfn[M],low[M],stk[M],scc[M],sccnt; bool ins[M]; void ade(int s,int t){ G[ecnt]={t,hd[s]}; hd[s]=ecnt++; }void lnk(int a,int b,int c,int d){ a=2*a+b; c=2*c+d; ade(a,c); ade(c^1,a^1); }void targan(int x){ dfn[x]=low[x]=++dfc; stk[++*stk]=x; ins[x]=1; for(int e=hd[x];e;e=G[e].n) if(!dfn[G[e].t]){ targan(G[e].t); cmin(low[x],low[G[e].t]); }else if(ins[G[e].t]) cmin(low[x],dfn[G[e].t]); if(dfn[x]==low[x]){ ++sccnt; while(stk[*stk]!=x){ scc[stk[*stk]]=sccnt; ins[stk[(*stk)--]]=0; } scc[stk[*stk]]=sccnt; ins[stk[(*stk)--]]=0; } } } namespace gph{ std::vector<int> G[NV+5]; int dfn[NV+5],dfc,dfseq[NV+5],siz[NV+5],top[NV+5],pr[NV+5],dep[NV+5],son[NV+5]; void dfs1(int x,int p){ siz[x]=1; dep[x]=dep[pr[x]=p]+1; for(int t:G[x]) if(t!=p){ dfs1(t,x); siz[x]+=siz[t]; if(siz[t]>siz[son[x]]) son[x]=t; } }void dfs2(int x,int tp){ top[x]=tp; dfseq[dfn[x]=++dfc]=x; if(!son[x]) return; dfs2(son[x],tp); for(int t:G[x]) if(!top[t]) dfs2(t,t); }int lca(int x,int y){ while(top[x]!=top[y]) if(dep[top[x]]>dep[top[y]]) x=pr[top[x]]; else y=pr[top[y]]; return dep[x]<dep[y]?x:y; } } struct UFS{ int f[NV+5]; void init(int n){ for(int i=1;i<=n;++i) f[i]=-1; }int fd(int x){ while(f[x]>=0&&f[f[x]]>=0) x=f[x]=f[f[x]]; return f[x]<0?x:f[x]; }bool un(int x,int y){ if((x=fd(x))==(y=fd(y))) return 0; if(f[x]<f[y]){ f[x]+=f[y]; f[y]=x; }else{ f[y]+=f[x]; f[x]=y; } return 1; } }; namespace xm{ std::vector<std::tuple<int,int,int> > h[NV+5]; UFS g[NV+5]; int vis[NV+5][NV+5],bl[NV+5][NV+5],nw[NV+5]; void _(){ int N,M,Q; //scanf("%d%d%d",&N,&M,&Q); io::gi(N); io::gi(M); io::gi(Q); for(int i=1,u,v;i<N;++i){ io::gi(u); io::gi(v); gph::G[u].push_back(v); gph::G[v].push_back(u); } gph::dfs1(1,0); gph::dfs2(1,1); for(int i=1;i<=N;++i) g[i].init(N); for(int i=1;i<=M;++i){ int k; io::gi(k); while(k--){ int x; io::gi(x); ++vis[i][x]; } for(int j=N;j>1;--j) vis[i][gph::pr[gph::dfseq[j]]]+=vis[i][gph::dfseq[j]]; } for(int i=1;i<=Q;++i){ int vs,ss; io::gi(vs); std::vector<int> vv(vs); for(int&x:vv) io::gi(x); vv.push_back(vv[0]); io::gi(ss); std::vector<int> sv(ss); for(int&x:sv) io::gi(x); for(int j=0;j<vs;++j){ int u=gph::lca(vv[j],vv[j+1]); for(int k=0;k<ss-1;++k) h[u].emplace_back(vv[j],sv[k],sv[k+1]); } } for(int i=1;i<=N;++i){ const int u=gph::dfseq[i]; for(auto t:h[u]){ int p,a,b; std::tie(p,a,b)=t; for(;p!=u&&g[p].un(a,b);p=gph::pr[p]); } } int Vcnt=0; for(int i=2;i<=N;++i){ for(int j=1;j<=M;++j) if(g[i].fd(j)==j) bl[j][i]=++Vcnt; for(int j=1;j<=M;++j) bl[j][i]=bl[g[i].fd(j)][i]; } for(int i=1;i<=M;++i){ memcpy(nw,bl[i],sizeof*bl); for(int j=N;j>1;--j){ const int u=gph::dfseq[j],v=bl[i][u]; if(!vis[i][u]) sat::lnk(v,1,v,0); else if(vis[i][u]==vis[i][1]) sat::lnk(v,0,v,1); else{ int z=nw[gph::pr[u]]; sat::lnk(v,1,z,1); if(gph::dfn[gph::pr[u]]+1!=gph::dfn[u]){ ++Vcnt; sat::lnk(Vcnt,1,v,0); sat::lnk(Vcnt,1,z,1); nw[gph::pr[u]]=Vcnt; } } } } for(int i=2;i<=2*Vcnt+1;++i) if(!sat::dfn[i]) sat::targan(i); for(int i=1;i<=M;++i) for(int j=2;j<=N;++j){ int x=bl[i][j]; if(sat::scc[x*2]==sat::scc[x*2+1]){ puts("-1"); return; } } for(int i=1;i<=M;++i){ int rt=1; for(int j=2;j<=N;++j){ int x=bl[i][gph::dfseq[j]]; if(sat::scc[2*x]>sat::scc[2*x+1]) rt=gph::dfseq[j]; } printf("%d ",rt); } puts(""); } } int main(){ xm::_(); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!