noip模拟38 c <点分治>
点分治
(: 今天考到了,刚被迫学的,所以有解释不太对的地方还请指教。)
一种高效的优化方式,
@ICEY :“ 在执行完点分治的操作后,新图中两点的lca一定在原图中两点间的简单路径上。 ”
在点分治优化完后,新图大致被分为 \(log\) 层,
若把原图看为一个序列,我们每次把中点当成我们新的分治点,用原分治点向新分治点连边,然后继续向下递归,直至均被 \(vis\)。大致就是下图形式,
转换成树上,其实我们就是要找子树中节点 \(size\) 最接近总数一半的一点(也就是树的重心)作为新的分治点,与序列原理相同。
在建完图后,我们就得到了大致均衡的新图,此时对待一些树上问题我们就可以高效率得求出所需答案。n这么小?再来10万!
附点分治的基础代码:
查找重心
inline void dfsll(int now,int pre,int tot,int &rt) {
int maxson=0; size[now]=1;
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]||to==pre) continue;
dfsll(to,now,tot,rt);
size[now]+=size[to];
cmax(maxson,size[to]);
}
if(maxson<=tot/2&&tot-size[now]<=tot/2) rt=now;
}
好像没了... 其余根据题意来好了
本题题解
简单环的最长长度不超过2,其实我们把重边删去后,原图就是一棵树。
颜色数较多的边我们仅需存任意三个颜色即可,也就是保证了至少有一种颜色能够均不与两边匹配,显然对答案是没有影响的。
对于转移,我们想要知道的只是序列左右两端的颜色,
然后我们就可以针对每个点的lca进行离线处理
设 \(dp_{i,j,k}\) : 表示从分治点(lca)到 \(i\) 点的序列,最前端为 \(j\) 颜色,末端为 \(k\) 颜色的最优决策,转移时分别枚举两个线段就行了,
初始边的处理代码:
// unique操作只是把重复元素提到了原序列末尾,但原序列总长度不变,
//所以还需要将去重后返回的末尾迭代器与原序列末尾之间的元素删去。
inline void dfsl(int now,int pre) {
sort(sl[now].begin(),sl[now].end());
sl[now].erase(unique(sl[now].begin(),sl[now].end()),sl[now].end());
for(re int i=0,j,to;i<sl[now].size();i=j) {
to=sl[now][i].first;
j=i+1;
while(j<sl[now].size()&&sl[now][j].first==to) j++;
if(to==pre) continue;
edge[now].push_back(pir(to,vector<int>()));
edge[to].push_back(pir(now,vector<int>()));
for(re int k=i;k<j&&k<i+3;k++) {
edge[now].back().second.push_back(sl[now][k].second);
edge[to].back().second.push_back(sl[now][k].second);
}
dfsl(to,now);
}
}
code
#include <bits/stdc++.h>
#define re register
#define int long long
#define pir make_pair
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
using namespace std;
const int maxn=200010;
const int INF=1e15;
inline int read() {
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1;ch=getchar(); }
while(ch>='0'&&ch<='9') { s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
int n,m,q;
vector<pair<int,int> > sl[maxn];
vector<pair<int,vector<int> > > edge[maxn];
namespace WORK {
inline void cmax(int &a,int b) { if(a<b) a=b; }
struct QUERY { int id,a,b; }; bool vis[maxn];
int f[22][maxn],ansl[maxn],dep[maxn],size[maxn],dp[maxn][3][3];
vector<QUERY>vec[maxn];
inline void dfsl(int now,int pre) {
sort(sl[now].begin(),sl[now].end());
sl[now].erase(unique(sl[now].begin(),sl[now].end()),sl[now].end());
for(re int i=0,j,to;i<sl[now].size();i=j) {
to=sl[now][i].first;
j=i+1;
while(j<sl[now].size()&&sl[now][j].first==to) j++;
if(to==pre) continue;
edge[now].push_back(pir(to,vector<int>()));
edge[to].push_back(pir(now,vector<int>()));
for(re int k=i;k<j&&k<i+3;k++) {
edge[now].back().second.push_back(sl[now][k].second);
edge[to].back().second.push_back(sl[now][k].second);
}
dfsl(to,now);
}
}
inline void dfsll(int now,int pre,int tot,int &rt) {
int maxson=0; size[now]=1;
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]||to==pre) continue;
dfsll(to,now,tot,rt);
size[now]+=size[to];
cmax(maxson,size[to]);
}
if(maxson<=tot/2&&tot-size[now]<=tot/2) rt=now;
}
inline void sleve0(int now,int tot,int pre) {
dfsll(now,0,tot,now);
vis[now]=1;
f[0][now]=pre;
dep[now]=dep[f[0][now]]+1;
for(re int i=1;i<=20;i++) f[i][now]=f[i-1][f[i-1][now]];
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]) continue;
if(size[to]<size[now]) sleve0(to,size[to],now);
else sleve0(to,tot-size[now],now);
}
}
inline int lca(int a,int b) {
if(dep[a]<dep[b]) swap(a,b);
for(re int i=20;i>=0;i--)
if(dep[f[i][a]]>=dep[b]) a=f[i][a];
if(a==b) return a;
for(re int i=20;i>=0;i--)
if(f[i][a]!=f[i][b]) a=f[i][a],b=f[i][b];
return f[0][a];
}
vector<int> vfa[maxn]; int tmp[maxn];
inline void dfs1(int now,int pre,int rt) {
tmp[now]=rt;
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]||to==pre) continue;
vfa[to]=edge[now][i].second;
for(re int j=0;j<vfa[rt].size();j++)
for(re int l=0;l<vfa[to].size();l++) {
dp[to][j][l]=-INF;
for(re int k=0;k<vfa[now].size();k++)
cmax(dp[to][j][l],dp[now][j][k]+(vfa[now][k]!=vfa[to][l]));
}
dfs1(to,now,rt);
}
}
inline void sleve1(int now,int tot) {
dfsll(now,0,tot,now);
vis[now]=1;
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]) continue;
vfa[to]=edge[now][i].second;
for(re int j=0;j<vfa[to].size();j++)
for(re int l=0;l<vfa[to].size();l++)
dp[to][j][l]=(j==l)? 0:-INF;
dfs1(to,now,to);
}
for(re int i=0,a,b;i<vec[now].size();i++) {
a=vec[now][i].a; b=vec[now][i].b;
if(b==now) swap(a,b);
if(a==b) ansl[vec[now][i].id]=0;
else if(a==now) {
for(re int j=0;j<vfa[tmp[b]].size();j++)
for(re int l=0;l<vfa[b].size();l++)
cmax(ansl[vec[now][i].id],dp[b][j][l]);
++ansl[vec[now][i].id];
} else {
for(re int j=0;j<vfa[a].size();j++)
for(re int l=0;l<vfa[tmp[a]].size();l++)
for(re int k=0;k<vfa[tmp[b]].size();k++)
for(re int h=0;h<vfa[b].size();h++)
cmax(ansl[vec[now][i].id],dp[a][l][j]+dp[b][k][h]+(vfa[tmp[a]][l]!=vfa[tmp[b]][k]));
++ansl[vec[now][i].id];
}
}
for(re int i=0,to;i<edge[now].size();i++) {
if(vis[to=edge[now][i].first]) continue;
if(size[to]<=size[now]) sleve1(to,size[to]);
else sleve1(to,tot-size[now]);
}
}
inline int main() {
dfsl(1,0); sleve0(1,n,0);
q=read();
for(re int i=1,a,b;i<=q;i++) { a=read(),b=read(); vec[lca(a,b)].push_back((QUERY){i,a,b}); }
for(re int i=1;i<=n;i++) vis[i]=0;
sleve1(1,n);
for(re int i=1;i<=q;i++) printf("%lld\n",ansl[i]);
return 0;
}
}
signed main(void) {
freopen("c5_1.in","r",stdin);
//freopen("cs.txt","w",stdout);
n=read(),m=read();
for(re int i=1,u,v,w;i<=m;i++) {
u=read(),v=read(),w=read();
sl[u].push_back(pir(v,w)); sl[v].push_back(pir(u,w));
}
WORK::main();
}