《陕西师范大学第九届ACM程序设计竞赛》

A:其实并不是特别难的一个题,想的太复杂了。

首先,我们把关系看成能够传递的,那么我们并查集统计连通块之后。

可以发现,对于一个连通块,最优方案肯定只有1个人会没朋友。

因为一直把和当前删的点有关系的人的人删进去,最后肯定就能删完全部点,并且保证只有第一个进去的人没朋友。

因为要保证字典序最小,我们每次合并并查集的时候,都让大的以小的为父节点,那么最后这个集合的根,肯定就是这个集合里最小的点。

那么我们先把根都扔到小顶堆里,然后在把和堆顶相邻的没删的元素放入,因为小顶堆,会自己保证最小字典序。

// Author: levil
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e6+5;
const int M = 1e4;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e9
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    /*const int BUF_SIZE = 8e7;
    inline char nc(){
        static char buf[BUF_SIZE],*p1 = buf,*p2 = buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF_SIZE,stdin),p1==p2)?EOF:*p1++;
    }*///file close.
    inline int read(){  
        int x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
    void print(int x){
        if(x < 0){x = -x;putchar('-');}
        if(x > 9) print(x/10);
        putchar(x%10+'0');
    }
}
using namespace FASTIO;
void FRE(){
/*freopen("data1.in","r",stdin);
freopen("date1.out","w",stdout);*/}

vector<int> G[N];
int fa[N],vis[N];
int Find(int x)
{
    return x == fa[x] ? x : fa[x] = Find(fa[x]);
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n,m;n = read(),m = read();
        for(rg int i = 1;i <= n;++i) G[i].clear(),fa[i] = i,vis[i] = 0;
        while(m--)
        {
            int u,v;u = read(),v = read();
            G[u].push_back(v);
            G[v].push_back(u);
            int xx = Find(u),yy = Find(v);
            if(xx > yy) fa[xx] = yy;
            else fa[yy] = xx;
        }
        priority_queue<int,vector<int>,greater<int> > Q;
        int cnt = 0;
        for(rg int i = 1;i <= n;++i) if(fa[i] == i) Q.push(i),vis[i] = 1,cnt++;
        vector<int> ans;
        while(!Q.empty())
        {
            int u = Q.top();
            Q.pop();
            ans.push_back(u);
            for(auto v : G[u])
            {
                if(!vis[v]) vis[v] = 1,Q.push(v); 
            }
        }
        printf("%d\n",cnt);
        for(int i = 0;i < ans.size();++i) printf("%d%c",ans[i],i == ans.size()-1 ? '\n' : ' ');
    }
    system("pause");     
    return 0;
}
View Code

C:考虑m个点里选择n-1个不同的点,然后除了最大的值外,其他值都能出现两次,那么有出现两次的点有(n-2)种。

那么重复出现的点在左右两边交换的时候,还是不改变。对于出现一次的点(除了最大值),它在最大值左右两边都是不同的方案。

那么ans = C(m,n-1) * (n-2) * 2^(n-3)

// Author: levil
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 2e5+5;
const int M = 1e4;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e9
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    /*const int BUF_SIZE = 8e7;
    inline char nc(){
        static char buf[BUF_SIZE],*p1 = buf,*p2 = buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF_SIZE,stdin),p1==p2)?EOF:*p1++;
    }*///file close.
    inline int read(){  
        int x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
    void print(int x){
        if(x < 0){x = -x;putchar('-');}
        if(x > 9) print(x/10);
        putchar(x%10+'0');
    }
}
using namespace FASTIO;
void FRE(){
/*freopen("data1.in","r",stdin);
freopen("date1.out","w",stdout);*/}

LL f[N];
void init()
{
    f[0] = 1;for(rg int i = 1;i < N;++i) f[i] = f[i-1]*i%Mod;
}
LL quick_mi(LL a,LL b)
{
    LL re = 1;
    while(b)
    {
        if(b&1) re = re*a%Mod;
        a = a*a%Mod;
        b >>= 1;
    }
    return re;
}
LL inv(LL n){return quick_mi(n,Mod-2);}
LL C(LL n,LL m){return f[n]*inv(f[m])%Mod*inv(f[n-m])%Mod;}
int main()
{
    init();
    int n,m;n = read(),m = read();
    if(n <= 2) printf("0\n");
    else{
        LL ma = C(m,n-1);//m个数里选n-1个不一样的。
        LL ans = ma*(n-2)%Mod*quick_mi(2,n-3)%Mod;
        printf("%lld\n",ans);
    }
   // system("pause");     
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zwjzwj/p/13517160.html