• LOJ3280 「JOISC 2020 Day4」首都 (Capital City) 题解


    好像主流做法是点分治,然后我在考场上写了一个时间和空间常数都很大的 tarjan + 倍增优化建图的垃圾做法。


    下面介绍我的垃圾做法:
    首先考虑对于两个颜色 (x)(y) ,若选了颜色 (x) 就必须选 (y) ,则在图中连一条 (x ightarrow y) 的边。那么可以对这个图进行 tarjan 缩点求出 scc,然后答案就是没有出边的 scc 中包含点数最少的那个。 (证明略)

    然后考虑如何 (n^2) 地建图:
    显然,若一个点 (x) 在颜色相同的两个点 (y,z) 之间的最短路上,则需要有一条边 (col_y ightarrow col_x),然后考虑设所有颜色为 (c) 的点的集合为 (S_c) , (S_c) 中所有点的 LCA 是 (l) ,那么 (forall x in S_c)(y)(l)(x) 之间的路径上,有一条边 (c ightarrow col_y)

    那么对于一个点 (x) ,他连出去的边分布在 (|S_x|) 条链上,只要对它进行倍增优化建图即可在 (O(nlog n)) 的复杂度内解决这个问题。

    (这个做法真的又慢又费空间,虽然他时间和空间的复杂度都是 (O(nlog n)) 的,但它在考场上跑了 2s,空间用了 400+MB,但也有可能是 STL 过度使用的原因)

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    #define Fast_IO ios::sync_with_stdio(false);
    #define DEBUG fprintf(stderr,"Running on Line %d in Function %s
    ",__LINE__,__FUNCTION__)
    #define fir first
    #define sec second
    #define mod 998244353
    #define INF 0x3fffffff
    #define ll long long
    inline int read()
    {
    	char ch=getchar(); int nega=1; while(!isdigit(ch)) {if(ch=='-') nega=-1; ch=getchar();}
    	int ans=0; while(isdigit(ch)) {ans=ans*10+ch-48;ch=getchar();}
    	if(nega==-1) return -ans;
    	return ans;
    }
    typedef pair<int,int> pii;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int sub(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1LL*x*y%mod;}
    const int N=200005*20;
    const int M=200005;
    vector<int> G[M];
    int col[M],n,m,lg[N];
    vector<int> v[M];
    int l[M];
    int siz[M],dep[M],son[M],top[M],fa[M],f[M][20];
    int cnt;
    int t[M][20];
    void dfs1(int u,int Fa)
    {
    	siz[u]=1; fa[u]=Fa; dep[u]=dep[Fa]+1; f[u][0]=Fa;
    	for(int v:G[u])
    	{
    		if(v==Fa) continue;
    		dfs1(v,u);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]]) son[u]=v;
    	}
    }
    void dfs2(int u,int topf)
    {
    	top[u]=topf;
    	if(!son[u]) return ;
    	dfs2(son[u],topf);
    	for(int v:G[u])
    	{
    		if(v==fa[u]||v==son[u]) continue;
    		dfs2(v,v);
    	}
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
    		else v=fa[top[v]];
    	}
    	return dep[u]>dep[v]?v:u;
    }
    vector<int> H[N];
    int dgr[N],dfn[N],low[N],vis[N],_cnt,bel[N],_sum,dat[N];
    stack<int> s;
    void tarjan(int u)
    {
    	if(!u) return ;
    	low[u]=dfn[u]=++_cnt;
    	vis[u]=1; s.push(u);
    	for(int v:H[u])
    	{
    		if(v==u) continue;
    		if(!dfn[v])
    		{
    			tarjan(v);
    			low[u]=min(low[u],low[v]);
    		}
    		else if(vis[v]) low[u]=min(low[u],dfn[v]);
    	}
    	if(low[u]==dfn[u])
    	{
    		_sum++;
    		while(1)
    		{
    			int x=s.top(); s.pop();
    			bel[x]=_sum;
    			if(x<=m) dat[_sum]++;
    			vis[x]=0;
    			if(x==u) break;
    		}
    	}
    }
    int jump(int u,int dep)
    {
    	for(int i=18;i>=0;i--)
    	{
    		if((1<<i)<=dep)
    		{
    			dep-=1<<i;
    			u=f[u][i];
    		}
    	}
    	return u;
    }
    void addedge(int fr,int u,int v)
    {
    	int x=lg[dep[v]-dep[u]+1];
    	H[fr].pb(t[v][x]);
    	int qaq=jump(v,(dep[v]-dep[u]+1)-(1<<x));
    	H[fr].pb(t[qaq][x]);
    }
    signed main()
    {
    	lg[1]=0; for(int i=2;i<N;i++) lg[i]=lg[i/2]+1;
    	cin>>n>>m;
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		G[u].pb(v),G[v].pb(u);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		col[i]=read();
    		v[col[i]].pb(i);
    	}
    	dfs1(1,0),dfs2(1,1);
    	for(int i=1;i<=m;i++)
    	{
    		l[i]=v[i][0];
    		for(int j=1;j<(int)v[i].size();j++) l[i]=lca(l[i],v[i][j]);
    	}
    	for(int i=1;i<=18;i++)
    	{
    		for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
    	}
    	for(int i=1;i<=n;i++) t[i][0]=col[i];
    	cnt=m;
    	for(int i=1;i<=18;i++)
    	{
    		for(int j=1;j<=n;j++)
    		{
    			t[j][i]=++cnt;
    			H[t[j][i]].pb(t[j][i-1]);
    			H[t[j][i]].pb(t[f[j][i-1]][i-1]);
    		}
    	}
    	for(int i=1;i<=n;i++)
    	{
    		addedge(col[i],l[col[i]],i);
    	}
    	for(int i=1;i<=cnt;i++) if(!dfn[i]) tarjan(i);
    	for(int i=1;i<=cnt;i++)
    	{
    		for(int v:H[i])
    		{
    			if(bel[i]!=bel[v]) dgr[bel[i]]++;
    		}
    	}
    	int ans=INF;
    	for(int i=1;i<=_sum;i++) if(!dgr[i]&&dat[i]>0) ans=min(ans,dat[i]);
    	cout<<ans-1<<endl;
    	return 0;
    }
    
  • 相关阅读:
    CSU 1554 SG Value (集合类的学习)
    CSUOJ 1542 线段树解决括号反向问题
    POJ 1679 判最小生成树的不唯一性 或 利用次小生成树求解
    HDU1074 Doing Homework 状态压缩dp
    POJ 2479 两段连续最大和
    HDU1024 多段最大和 DP
    HDU 4803 贪心
    POJ 3469 网络流最小割
    SPOJ ARCTAN
    COJ 1163 乘法逆元的求解
  • 原文地址:https://www.cnblogs.com/wasa855/p/12569004.html
Copyright © 2020-2023  润新知