集美大学校赛 B,C

B - 小M的游戏

思路:考虑最短路径上的博弈,对于sg(n),设定其为必败态,那么我们通过转移求出初始点为必败态还是必胜态即可。
我们可以预处理出从 n 到任意点的最短路,对于点 u ,其后继节点能否被更新的条件为,两点之间的路径是否为最短路,然后运用sg函数去判断即可。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9+7;
const int maxv = 4e6 + 5;
// #define endl "\n"

vector<pll> e[N];

void add(int u,int v,int w)
{
    e[u].push_back({v,w});
    e[v].push_back({u,w});
}
int n,m;
int f[N];
ll d[N];
bool st[N];

void dijk()
{
    d[n]=0;
    priority_queue<pll,vector<pll>,greater<pll> > q;
    q.push({d[n],n});
    while(!q.empty()){
        auto [dis,u]=q.top();
        q.pop();
        for(auto [v, w]: e[u]){
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                q.push({d[v],v});
            }
        }
    }
}

int sg(int x)
{
    if(f[x]!=-1) return f[x];
    ll res=d[x];
    set<int> s;
    for(auto [u,w]: e[x]){
        if(d[x]-d[u]==w) s.insert(sg(u));
    }
    for(int i=0;;i++){
        if(!s.count(i)){
            return f[x]=i;
        }
    }
}


void solve()
{

    cin>>n>>m;
    for(int i=1;i<=n;i++){
        e[i].clear();
        f[i]=-1;
        d[i]=2e18,st[i]=0;
    }
    for(int i=1;i<=m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        add(u,v,w);
    }
    dijk();
    int t=sg(1);
	if(t)cout << "Little M is the winner." << endl;
	else cout << "Little I is the winner." << endl;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    cin>>t;
    while(t--){
        solve();
    }
    system("pause");
    return 0;
}

C - 方格染色

思路:考虑组合数学,我们考虑将k个黑块分成 1 , 2 , 3 , 4...... k − 1 , k 1,2,3,4......k-1,k 1,2,3,4......k1,k块进行单独考虑,然后根据加法原理,将其相加即可。
考虑当 k = i k=i k=i的情况:
即现在一共有 n 个空,然后把 i 个球放入 n-( k - i ) 个空中,要求两两不相邻,一共有多少种方法。
即运用隔板法,对于A个球,B个空,我们会空出B-A个空格,那么我们将其视为隔板,由此可以插入的位置为 B-A+1个,由此,将 A个球放入B个空的方法为 C B − A + 1 A C_{B-A+1}^{A} CBA+1A
再次考虑,我们把k个块分成i块进行单独考虑,其拆分方式有多种,那么将其抽象为一般问题:
即给定A个球,将其分给B个人(每个人最少获得一个),有多少种分法。
我们将A个球看作一个序列,将其分为B个人,我们将其看作把这个序列分成几段,所以我们想要将其分成B段的话,我们需要切B-1刀,而我们能进行切割的地方为A-1个,所以综上所述,答案为 C A − 1 B − 1 C_{A-1}^{B-1} CA1B1
再次考虑,分成i块,我们单独看每一块,其内部有两种排序方法。
综上所述,根据乘法原理,我们将其相乘即可。

#include 

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9+7;
const int maxv = 4e6 + 5;
// #define endl "\n"

template<const int T>
struct ModInt {
    const static int mod = T;
    int x;
    ModInt(int x = 0) : x(x % mod) {}
    ModInt(long long x) : x(int(x % mod)) {} 
    int val() { return x; }
    ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
    ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
    ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
    ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
    bool operator == (const ModInt &a) const { return x == a.x; };
    bool operator != (const ModInt &a) const { return x != a.x; };
    void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }
    void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }
    void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
    void operator /= (const ModInt &a) { *this = *this / a; }
    friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
    friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
    friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}
    friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}
    friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
    friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}

    ModInt pow(int64_t n) const {
        ModInt res(1), mul(x);
        while(n){
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0;
        while (b) {
            int t = a / b;
            a -= t * b; swap(a, b);
            u -= t * v; swap(u, v);
        }
        if (u < 0) u += mod;
        return u;
    }
    
};
using mint = ModInt<998244353>;
// constexpr mod = ...;
// using Mint = modint;
struct Fact {
    vector<mint> fact, factinv,er;
    const int n;
    Fact(const int& _n) : n(_n), fact(_n + 1, mint(1)), factinv(_n + 1),er(_n+1,mint(1)) {
        for (int i = 1; i <= n; ++i) fact[i] = fact[i - 1] * i;
        for (int i = 1; i <= n; i++) er[i]=er[i-1]*2;
        factinv[n] = fact[n].inv();
        for (int i = n; i; --i) factinv[i - 1] = factinv[i] * i;
    }
    mint C(const int& n, const int& k) {
        if (n < 0 || k < 0 || n < k) return 0;
        return fact[n] * factinv[k] * factinv[n - k];
    }
    mint A(const int& n, const int& k) {
        if (n < 0 || k < 0 || n < k) return 0;
        return fact[n] * factinv[n - k];
    }
    mint E(const int& n){
        return er[n];
    }
};

Fact z(N-2);
void solve()
{
    int n,k;
    cin>>n>>k;
	if(k == 0)
	{
		cout << 1 << endl;
		return;
	}
    mint ans=0;
    int d=n-k+1;
    for(int i=1;i<=k;i++){
        mint t1=z.C(d,i)*z.C(k-1,i-1)*z.E(i);
        ans+=t1;
    }
    cout<<ans<<endl;

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t=1;
    cin>>t;
    while(t--){
        solve();
    }
    system("pause");
    return 0;
}

你可能感兴趣的:(组合数学,图论,c语言,算法,c++)