B - 小M的游戏
思路:考虑最短路径上的博弈,对于sg(n),设定其为必败态,那么我们通过转移求出初始点为必败态还是必胜态即可。
我们可以预处理出从 n 到任意点的最短路,对于点 u ,其后继节点能否被更新的条件为,两点之间的路径是否为最短路,然后运用sg函数去判断即可。
cpp
#include <bits/stdc++.h>
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......k−1,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} CB−A+1A。
再次考虑,我们把k个块分成i块进行单独考虑,其拆分方式有多种,那么将其抽象为一般问题:
即给定A个球,将其分给B个人(每个人最少获得一个),有多少种分法。
我们将A个球看作一个序列,将其分为B个人,我们将其看作把这个序列分成几段,所以我们想要将其分成B段的话,我们需要切B-1刀,而我们能进行切割的地方为A-1个,所以综上所述,答案为 C A − 1 B − 1 C_{A-1}^{B-1} CA−1B−1。
再次考虑,分成i块,我们单独看每一块,其内部有两种排序方法。
综上所述,根据乘法原理,我们将其相乘即可。
cpp
#include <bits/stdc++.h>
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<mod>;
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;
}