[uoj751]神隐

假设有\(2k\)次询问,考虑让每条边恰在\(k\)次询问中出现

对于\(k\)的上界,均满足\({2k\choose k}\ge n-1\),即可令每条边出现状态不全相同

此时,对于任意两点\((x,y)\),有\((x,y)\in E\iff x,y\)恰在\(k\)次询问中连通

\(x,y\)连通\(\iff x\)\(y\)路径上所有边均被选,当(路径上)边数\(\ge 2\)时交\(<k\)

暴力实现即可做到\(o(n^{2}\log n)\),参考这里的算法四优化后,可以做到\(o(n2^{k})\)

换一个角度,对于任意一点\(x\),有\(x\)度数为\(1\iff x\)恰在\(k\)次询问中为"孤立点"

\(x\)为孤立点\(\iff\)\(x\)相邻的边均未被选,当(与\(x\)相邻的)边数\(\ge 2\)时并\(>k\)

同时,其余\(k\)次询问\(x\)所在连通块的交大小为\(2\),且另一个点即与\(x\)相邻的点

记与\(x\)相邻的点为\(y\),这\(k\)次询问中\((x,y)\)均被选,则其余边总存在某次未被选

暴力实现即可做到\(o(n^{2}\log n)\),瓶颈在于求交,下面考虑优化:

考虑所有\(x\)被删除顺序的逆序,即以最后所剩下的点开始搜索的bfs序

此时,与\(x\)相邻的点即这\(k\)次询问 \(x\)所在连通块中bfs序最小的点 中bfs序最大的点

\(x\)的父亲为\(y\),其在这\(k\)次询问中均与\(x\)连通,即最终的点bfs序不超过\(y\)

\(y\)的父亲为\(z\),则\((y,z)\)未被选时bfs序最小的点即\(y\),因此最终的点bfs序不小于\(y\)

时间复杂度为\(o(n\log n)\),可以通过

#include<bits/stdc++.h>
#include "tree.h"
using namespace std;
#define N 150000
#define ll long long
int n,k,pos[N][30],sz[30][N],cnt[N],dfn[N],vis[N][30],mn[30][N];
ll sum[30][N];queue<int>q;vector<int>v[30];
vector<pair<int,int> >ans;vector<vector<int> >res[30];
vector<pair<int,int> >solve(int nn){
    n=nn,k=(n<=2000 ? 7 : 10);
    for(int i=0;i<(k<<1);i++)v[i].resize(n-1);
    for(int S=0,j=0;j<n-1;S++)
        if (__builtin_popcount(S)==k){
            for(int i=0;i<(k<<1);i++)
                if ((S>>i)&1)v[i][j]=1;
            j++;
        }
    for(int i=0;i<(k<<1);i++){
        if (!v[i].empty())res[i]=query(v[i]);
        else{
            for(int j=0;j<n;j++)res[i].push_back(vector<int>{j});
        }
        for(int t=0;t<res[i].size();t++){
            for(int j:res[i][t])pos[j][i]=t,sz[i][t]++,sum[i][t]+=j;
            if ((sz[i][t]==1)&&(++cnt[sum[i][t]]==k))q.push(sum[i][t]);
        }
    }
    while (!q.empty()){
        int j=q.front();q.pop();
        dfn[j]=n-dfn[n],dfn[n]++;
        for(int i=0;i<(k<<1);i++){
            int t=pos[j][i];sum[i][t]-=j;
            if ((--sz[i][t]==1)&&(++cnt[sum[i][t]]==k))q.push(sum[i][t]);
            if (!sz[i][t])vis[j][i]=1;
        }
    }
    for(int i=0;i<(k<<1);i++)
        for(int t=0;t<res[i].size();t++){
            int Mn=-1;
            for(int j:res[i][t])
                if ((Mn<0)||(dfn[Mn]>dfn[j]))Mn=j;
            mn[i][t]=Mn;
        }
    for(int j=0;j<n;j++){
        if (dfn[j]==1)continue;
        int to=-1;
        for(int i=0;i<(k<<1);i++)
            if (!vis[j][i]){
                int Mn=mn[i][pos[j][i]];
                if ((to<0)||(dfn[to]<dfn[Mn]))to=Mn;
            }
        ans.push_back(make_pair(j,to));
    }
    return ans;
}
posted @ 2022-08-09 09:27  PYWBKTDA  阅读(100)  评论(0编辑  收藏  举报