H 叁佰爱抠的序列(思维欧拉回路)

题:https://ac.nowcoder.com/acm/contest/4010/H

题意:找到最大的m,使得存在一个长度为n值域为 [1,m] 的序列,满足任意一组相邻关系都存在。输出为若n>2e6则直接输出m,否则另外输出构造的序列
分析:相邻关系都要有,那么我们可以联想到n^2的大概样子,然后就m个点完全图可以满足含有相邻关系,更有如果考虑最优,那么存在欧拉回路即可满足条件;

   因为m对于此题来说具有单调性,所以二分地考虑它,m为奇数时,每个点的度数为偶数,必定存在欧拉回路,,m为偶数时,我们利用让含欧拉回路图的性质俩个节点度数为奇数,其余为偶数,方法就是,俩个奇数度数节点连一条边即可;

   最后不足n的部分随便补数即可;

trick:由于欧拉回路是走边的且要回到起点,所以二分时应该对check部分加1

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define pb push_back
typedef long long ll;
const int inf=0x3f3f3f3f;
const int M=2e6+6;
struct node{
    int v,nextt,w;
}e[M<<1];
int head[M],tot;
vector<int>ans;
void addedge(int u,int v){
    e[tot].v=v;
    e[tot].nextt=head[u];
    e[tot].w=0;
    head[u]=tot++;
    e[tot].v=u;
    e[tot].nextt=head[v];
    e[tot].w=0;
    head[v]=tot++;
}
ll check(int x){
    if(x%2)
        return 1ll*x*(x-1)/2+1;///n是走的边,最后返回要边数加1
    else
        return 1ll*x*(x-1)/2+(x-2)/2+1;///同理加1
}
void dfs(int u){
    for(int &i=head[u];~i;i=e[i].nextt){
        if(e[i].w==1)
            continue;
        e[i].w=e[i^1].w=1;
        dfs(e[i].v);
    }
    ans.pb(u);
}
int main(){
    ll n;
    scanf("%lld",&n);
    ll l=1,r=2e9;
    int m;
    while(l<=r){
        int midd=(l+r)>>1;
        if(check(midd)<=n)
            m=midd,l=midd+1;
        else
            r=midd-1;
    }
    if(n>2e6){
        return printf("%d
",m),0;
    }
    for(int i=0;i<=m;i++)
        head[i]=-1;
    for(int i=1;i<=m;i++)
        for(int j=i+1;j<=m;j++)
            addedge(i,j);
    if(m%2==0){
        for(int i=3;i<=m;i+=2)
            addedge(i,i+1);
    }
    dfs(1);
    for(int i=ans.size();i<=n;i++)
        ans.pb(1);
    printf("%d
",m);
    for(int i=0;i<n;i++)
        if(i!=n-1) printf("%d ",ans[i]);
        else printf("%d
",ans[i]);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/12927240.html