小Y增员操直播群

题目大意

n个人,初始一个人一个联通块,编号都是0
每次两个联通块合并,假设较小联通块大小是x,较大联通块编号为y的会与较小联通块大小是y mod x的连边,然后编号变成y+x,接下来合并两个联通块。
最终所有人都在一个联通块内。
现在给你最终联通块中所有连边情况,已知一天里联通块合并可以并行,问最少多少天能合并出来。

做法

考虑一个递归过程,任意时刻联通块对应最终联通块一个编号区间。
我们要尝试分割它,满足题目所说的条件。
假设是分割0n1
n1连的最小编号是x,因为n1一定在较大联通块,所以x一定在较小联通块。
假设较小联通块大小为y,有(n1) mod y=x
如果两个联通块不相等,因为后面那个更大,显然可以找到(nx2) mod y=y1
那么nx2连向的最小编号就是分界线。
能找分界线本题显然就能解决。

#include<cstdio>
#include<algorithm>
#include<vector>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define pb push_back
using namespace std;
const int maxn=100000+10;
vector<int> e[maxn];
int i,j,k,l,t,n,m,tot,ans,ca;
bool czy;
int solve(int l,int r){
    if (l==r&&!e[r].size()) return 0;
    if (l>r||!e[r].size()) return -1;
    int mid=e[r].back();
    if (r-mid>mid-l+1){
        if (!e[r-(mid-l+1)].size()) return -1;
        int tmp=e[r-(mid-l+1)].back();
        if (tmp<mid) return -1;
        mid=tmp;
    }
    if (r-mid<mid-l+1||mid<l) return -1;
    int now=(r-mid-1)%(mid-l+1);
    int i;
    fd(i,r,mid+1){
        if (!e[i].size()) return -1;
        if (e[i].back()-l!=now) return -1;
        e[i].erase(e[i].end()-1);
        if (now==0) now=mid-l;else now--;
    }
    int j=solve(l,mid),k=solve(mid+1,r);
    if (j<0||k<0) return -1;
    else return max(j,k)+1;
}
int main(){
    freopen("gymnastics.in","r",stdin);freopen("gymnastics.out","w",stdout);
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d",&n,&m);
        fo(i,0,n-1) e[i].clear();
        fo(i,1,m){
            scanf("%d%d",&j,&k);
            if (j<k) swap(j,k);
            e[j].pb(k);
        }
        fo(i,0,n-1){
            sort(e[i].begin(),e[i].end());
            reverse(e[i].begin(),e[i].end());
        }
        ans=solve(0,n-1);
        printf("%d\n",ans);
    }
}
阅读更多
版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/80504605
个人分类: 模拟 构造
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭