AtCoder Grand Contest 046 簡要題解

從這裏開始

  達成成就:不看 agc 題解補完一場 agc。

  感覺是我做過的最無聊的一場 agc,沒有之一。讓我來回顧一下我做了什麼:

  • 大力猜結論
  • 大力猜結論
  • 好難啊,哦,沒看到 respectively
  • 大力猜結論
  • 大力猜結論
  • #include "1338E",大力猜結論

  爲什麼這場出題人不學習一下隔壁 global round 9。

Problem A Takahashikun, The Strider

  顯然答案是 $\frac{360}{(X, 360)}$。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

template <typename T>
boolean vmin(T& a, T b) {
	return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
	return (a < b) ? (a = b, true) : (false);
}

template <typename T>
T smax(T x) {
	return x;
}
template <typename T, typename ...K>
T smax(T a, const K &...args) {
	return max(a, smax(args...));
}

template <typename T>
T smin(T x) {
	return x;
}
template <typename T, typename ...K>
T smin(T a, const K &...args) {
	return min(a, smin(args...));
}

// debugging lib

#define VN(x) #x
#define Vmsg(x) VN(x) << " = " << (x)
#define printv(x) cerr << VN(x) << " = " << (x);
#define debug(...) fprintf(stderr, __VA_ARGS__);

template <typename A, typename B>
ostream& operator << (ostream& os, const pair<A, B>& z) {
	os << "(" << z.first << ", " << z.second << ')';
	return os;
}
template <typename T>
ostream& operator << (ostream& os, const vector<T>& a) {
	boolean isfirst = true;
	os << "{";
	for (auto z : a) {
		if (!isfirst) {
			os << ", ";
		}
		os << z;
		isfirst = false;
	}
	os << '}';
	return os;
}

#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define ll long long
#define ull unsigned long long

const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

#define pb push_back
#define eb emplace_back
#define fi first
#define sc second

template <typename T>
int vsize(vector<T>& x) {
	return (signed) x.size(); 
}

template <typename T>
int discrete(T* a, int* b, int n) {
    vector<T> v(a + 1, a + n + 1);
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
	for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
	return v.size();
}

mt19937 rng (time(NULL));

int randint(int l, int r) {
	return rng() % (r - l + 1) + l;
}

int X;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> X;
	cout << 360 / __gcd(X, 360) << '\n';
	return 0;
}

Problem B Extension

  考慮怎麼判斷一個局面是否可行。你相當於是要找一條折線只能向右和向上的折線,使得劃開的兩部分,一部分每列恰好有 1 個黑格子,一個每行恰好有 1 個黑格子。

  考慮對於必須要分給行的一個黑格子,那一列一定存在至少 2 個白格子。對於一列有至少 2 個白格子相當於是要求這個位置的折線高度至多爲多少。

  從右往左考慮每列,記錄最高的折線高度 dp。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

template <typename T>
boolean vmin(T& a, T b) {
	return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
	return (a < b) ? (a = b, true) : (false);
}

template <typename T>
T smax(T x) {
	return x;
}
template <typename T, typename ...K>
T smax(T a, const K &...args) {
	return max(a, smax(args...));
}

template <typename T>
T smin(T x) {
	return x;
}
template <typename T, typename ...K>
T smin(T a, const K &...args) {
	return min(a, smin(args...));
}

// debugging lib

#define VN(x) #x
#define Vmsg(x) VN(x) << " = " << (x)
#define printv(x) cerr << VN(x) << " = " << (x);
#define debug(...) fprintf(stderr, __VA_ARGS__);

template <typename A, typename B>
ostream& operator << (ostream& os, const pair<A, B>& z) {
	os << "(" << z.first << ", " << z.second << ')';
	return os;
}
template <typename T>
ostream& operator << (ostream& os, const vector<T>& a) {
	boolean isfirst = true;
	os << "{";
	for (auto z : a) {
		if (!isfirst) {
			os << ", ";
		}
		os << z;
		isfirst = false;
	}
	os << '}';
	return os;
}

#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define ll long long
#define ull unsigned long long

const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

#define pb push_back
#define eb emplace_back
#define fi first
#define sc second

template <typename T>
int vsize(vector<T>& x) {
	return (signed) x.size(); 
}

template <typename T>
int discrete(T* a, int* b, int n) {
    vector<T> v(a + 1, a + n + 1);
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
	for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
	return v.size();
}

mt19937 rng (time(NULL));

int randint(int l, int r) {
	return rng() % (r - l + 1) + l;
}

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a, int n) {
	int x, y;
	exgcd(a, n, x, y);
	return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
	public:
		int v;

		Z() : v(0) {	}
		Z(int x) : v(x){	}
		Z(ll x) : v(x % Mod) {	}

		friend Z operator + (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
		}
		friend Z operator - (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
		}
		friend Z operator * (const Z& a, const Z& b) {
			return Z(a.v * 1ll * b.v);
		}
		friend Z operator ~(const Z& a) {
			return inv(a.v, Mod);
		}
		friend Z operator - (const Z& a) {
			return Z(0) - a;
		}
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}
		friend boolean operator == (const Z& a, const Z& b) {
			return a.v == b.v;
		} 
};

Z<> qpow(Z<> a, int p) {
	Z<> rt = Z<>(1), pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

typedef Z<> Zi;

const int N = 3005;

int A, B, C, D;
Zi f[N][N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> A >> B >> C >> D;	
	int da = C - A, db = D - B;
	f[da + 1][db] = 1;
	for (int i = da; i; i--) {
		for (int j = 0; j <= db; j++) {
			f[i][j] += f[i + 1][j] * (j + B);
		}
		Zi sum = 0;
		for (int j = db; j--; ) {
			sum = sum * (i + A) + f[i + 1][j + 1];
			f[i][j] += sum * (j + B);	
		}
	}
	Zi ans = 0;
	for (int i = db; ~i; i--) {
		ans = ans * A + f[1][i];
	}
	cout << ans.v << '\n';
	return 0;
}

Problem C Shift

  考慮 0 分割開的若干連續段,每次操作相當於把後面某個連續段長度 - 1,前面某個連續段長度 + 1。dp 即可。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

template <typename T>
boolean vmin(T& a, T b) {
  return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
  return (a < b) ? (a = b, true) : (false);
}

template <typename T>
T smax(T x) {
  return x;
}
template <typename T, typename ...K>
T smax(T a, const K &...args) {
  return max(a, smax(args...));
}

template <typename T>
T smin(T x) {
  return x;
}
template <typename T, typename ...K>
T smin(T a, const K &...args) {
  return min(a, smin(args...));
}

// debugging lib

#define VN(x) #x
#define Vmsg(x) VN(x) << " = " << (x)
#define printv(x) cerr << VN(x) << " = " << (x);
#define debug(...) fprintf(stderr, __VA_ARGS__);

template <typename A, typename B>
ostream& operator << (ostream& os, const pair<A, B>& z) {
  os << "(" << z.first << ", " << z.second << ')';
  return os;
}
template <typename T>
ostream& operator << (ostream& os, const vector<T>& a) {
  boolean isfirst = true;
  os << "{";
  for (auto z : a) {
    if (!isfirst) {
      os << ", ";
    }
    os << z;
    isfirst = false;
  }
  os << '}';
  return os;
}

#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define ll long long
#define ull unsigned long long

const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

#define pb push_back
#define eb emplace_back
#define fi first
#define sc second

template <typename T>
int vsize(vector<T>& x) {
  return (signed) x.size(); 
}

template <typename T>
int discrete(T* a, int* b, int n) {
  vector<T> v(a + 1, a + n + 1);
  sort(v.begin(), v.end());
  v.erase(unique(v.begin(), v.end()), v.end());
  for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
  return v.size();
}

mt19937 rng (time(NULL));

int randint(int l, int r) {
  return rng() % (r - l + 1) + l;
}

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

const int N = 305;

int K;
char s[N];
Zi f[N][N], g[N][N];

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  scanf("%s%d", s + 1, &K);
  int n = strlen(s + 1);
  K = min(K, n);
  vector<int> pos0;
  for (int i = 1; i <= n; i++) {
    if (s[i] == '0') {
      pos0.push_back(i);
    }
  }
  pos0.insert(pos0.begin(), 0);
  pos0.push_back(n + 1);
  vector<int> len;
  for (int i = 1; i < (signed) pos0.size(); i++) {
    len.push_back(pos0[i] - pos0[i - 1] - 1);
  }
  reverse(len.begin(), len.end());
  f[0][0] = 1;
  int suml = 0;
  for (auto l : len) {
    suml += l;
    for (int i = 0; i <= suml; i++) {
      memset(g[i], 0, (suml + 1) << 2);
    }
    for (int i = 0; i <= suml; i++) {
      for (int j = 0; j <= suml; j++) {
        g[i][j] = f[i][j];
        if (i && j) g[i][j] += g[i - 1][j - 1];
      }
    }
    for (int i = suml; i > l; i--) {
      for (int j = suml; j > l; j--) {
        g[i][j] -= g[i - l - 1][j - l - 1];
      }
    }
    for (int i = 0; i <= suml; i++) {
      Zi sum = 0;
      for (int j = suml; ~j; j--) {
        sum += f[i][j + 1];
        g[i][j] += sum;
      }
    }
    for (int i = 0; i <= suml; i++) {
      for (int j = 0; j <= suml; j++) {
        f[i][j] = g[i][j];
      }
    }
  }
  Zi ans = 0;
  for (int i = 0; i <= K; i++) {
    ans += f[i][0];
  }
  printf("%d\n", ans.v);
  return 0;
}

Problem D Secret Passage

  顯然主要問題是給定一個串 $s$ 判斷是否可達。考慮把操作序列倒過來,相當於每次刪除一個字符在前面這一個,然後再加入一個任意字符(這兩個順序可以顛倒),判定能否到達初始的串 $S$。

  感性理解一下,只用記錄最長的等於 $S$ 的某個後綴的子序列長度,以及還有多少個 $0$,以及 $1$ 不在這裏面。

  證明在路上了。

  顯然填串 $s$ 的過程,和給定上述狀態判斷是否可行可以分開 dp。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

template <typename T>
boolean vmin(T& a, T b) {
  return (a > b) ? (a = b, true) : (false);
}
template <typename T>
boolean vmax(T& a, T b) {
  return (a < b) ? (a = b, true) : (false);
}

template <typename T>
T smax(T x) {
  return x;
}
template <typename T, typename ...K>
T smax(T a, const K &...args) {
  return max(a, smax(args...));
}

template <typename T>
T smin(T x) {
  return x;
}
template <typename T, typename ...K>
T smin(T a, const K &...args) {
  return min(a, smin(args...));
}

// debugging lib

#define VN(x) #x
#define Vmsg(x) VN(x) << " = " << (x)
#define printv(x) cerr << VN(x) << " = " << (x);
#define debug(...) fprintf(stderr, __VA_ARGS__);

template <typename A, typename B>
ostream& operator << (ostream& os, const pair<A, B>& z) {
  os << "(" << z.first << ", " << z.second << ')';
  return os;
}
template <typename T>
ostream& operator << (ostream& os, const vector<T>& a) {
  boolean isfirst = true;
  os << "{";
  for (auto z : a) {
    if (!isfirst) {
      os << ", ";
    }
    os << z;
    isfirst = false;
  }
  os << '}';
  return os;
}

#define pii pair<int, int>
#define pil pair<int, ll>
#define pli pair<ll, int>
#define ll long long
#define ull unsigned long long

const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

#define pb push_back
#define eb emplace_back
#define fi first
#define sc second

template <typename T>
int vsize(vector<T>& x) {
  return (signed) x.size(); 
}

template <typename T>
int discrete(T* a, int* b, int n) {
  vector<T> v(a + 1, a + n + 1);
  sort(v.begin(), v.end());
  v.erase(unique(v.begin(), v.end()), v.end());
  for (int i = 1; i <= n; i++) b[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
  return v.size();
}

mt19937 rng (time(NULL));

int randint(int l, int r) {
  return rng() % (r - l + 1) + l;
}


#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

const int N = 305;

int n;
char s[N];
bool f[N][N][N];
Zi g[N][N][N];

set<string> S;

void check(string t) {
  int len = t.length();
  int p = n, x = 0, y = 0;
  for (int i = len; i--; ) {
    if (t[i] == s[p]) {
      p--;
    } else {
      (t[i] == '0' ? x : y)++;
    }
  }
  if (!f[p][x][y]) {
    cerr << t << '\n';
  }
}

void dfs(string s) {
  if (S.count(s)) return;
  S.insert(s);
  check(s);
  if (s.length() < 2u)
    return;
  string t = s;
  t.erase(t.begin());
  t.erase(t.begin());
  char x = s[0], y = s[1];
  for (int i = 0; i < (signed) t.size(); i++) {
    string nt = t;
    nt.insert(nt.begin() + i, x);
    dfs(nt);
    nt = t;
    nt.insert(nt.begin() + i, y);
    dfs(nt);
  }
  string nt = t;
  nt += x;
  dfs(nt);
  nt = t;
  nt += y; 
  dfs(nt);
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> (s + 1);
  n = strlen(s + 1);
  f[0][0][0] = true;
  for (int i = 1; i <= n; i++) {
    for (int j = n; ~j; j--) {
      for (int k = n; ~k; k--) {
        if (i == n && !j && !k) continue;
        f[i][j][k] |= f[i - 1][j][k];
        if (s[i] == '0') f[i][j][k] |= f[i][j][k + 1];
        if (s[i] == '1') f[i][j][k] |= f[i][j + 1][k];
      } 
    }
    for (int j = 0; j <= n; j++) {
      for (int k = 0; k <= n; k++) {
        if (i > 1) {
          if ((s[i] == '0' || s[i - 1] == '0') && j) {
            f[i][j][k] |= f[i - 2][j - 1][k];
          }
          if ((s[i] == '0' && s[i - 1] == '0') && j) {
            f[i][j][k] |= f[i - 1][j - 1][k + 1];
          }
          if ((s[i] == '1' || s[i - 1] == '1') && k) {
            f[i][j][k] |= f[i - 2][j][k - 1];
          }
          if ((s[i] == '1' && s[i - 1] == '1') && k) {
            f[i][j][k] |= f[i - 1][j + 1][k - 1];
          }
        }
      }
    }
  }
  g[n + 1][0][0] = 1;
  for (int i = n + 1; i > 1; i--) {
    for (int j = 0; j <= n; j++) {
      for (int k = 0; k <= n; k++) {
        Zi v = g[i][j][k];
        if (!v.v)
          continue;
        g[i - 1][j][k] += v;
        if (s[i - 1] == '0')
          g[i][j][k + 1] += v;
        if (s[i - 1] == '1')
          g[i][j + 1][k] += v;
      }
    }
  }
  Zi ans = 0;
  for (int i = 0; i <= n; i++) {
    for (int j = 0; j <= i; j++) {
      for (int k = 0; k <= i; k++) {
        if (f[i][j][k]) {
          ans += g[i + 1][j][k];
//          cerr << g[i + 1][j][k].v << " " << i + 1 << " " << j << " "<< k << '\n';
        }
      }
    }
  }
//  dfs(string(s + 1));
  printf("%d\n", ans.v);
  return 0;
}

Problem E Permutation Cover

  顯然充要條件是 $\max a_i \leqslant 2\min a_i$。

  考慮枚舉每一位,枚舉它的值,判斷是否有一個合法序列的前綴爲它。

  感性理解一下:只用判斷補全一個包含它的排列,在再後面添加不超過 $K - 1$ 個數後,要求添加這些數現在都被某個排列覆蓋,剩下的所有數能否構成合法序列。

  證明在路上了。

 Code

#include <bits/stdc++.h>
using namespace std;

const int N = 105;

int n, m;
vector<int> c, p;
vector<bool> cov;
vector<int> vmi, vpmi;

bool chk0() {
  int mx = *max_element(c.begin(), c.end());
  int mi = *min_element(c.begin(), c.end());
  return mx <= 2 * mi;
}

int main() {
  scanf("%d", &m);
  c.resize(m);
  for (auto& x : c) {
    scanf("%d", &x);
    n += x;
  }
  if (!chk0()) {
    puts("-1");
    return 0;
  }
  p.resize(n);
  cov.resize(n, false);
  vmi.resize(n + 1, 0);
  vpmi.resize(n + 1, 0);
  for (int i = 0; i < n; i++) {
    for (int x = 0; x < m; x++) {
      if (!c[x]) continue;
      auto bc = c;
      auto bcov = cov;
      bc[x]--;
      p[i] = x;
      if (i >= m - 1) {
        vector<bool> vis(m, false);
        for (int j = 0; j < m; j++) {
          vis[p[i - j]] = true;
        }
        if (*min_element(vis.begin(), vis.end())) {
          fill(bcov.begin() + (i - m + 1), bcov.begin() + i + 1, true);
        }
      }
      int pr = i - 1;
      while (~pr && p[pr] != x)
        pr--;
      if (pr >= 0 && !*min_element(bcov.begin(), bcov.begin() + pr + 1)) {
        continue;
      }
      int R = i + 1;
      while (R && !bcov[R - 1])
        R--;
      bool flg = false;
      vector<bool> vis(m, false);
      int L = i + 1;
      while (L && !vis[p[L - 1]])
        vis[p[--L]] = true;
      int mi = 114514, mx = 0;
      for (int j = 0; j < m; j++) {
        mi = min(bc[j] - !vis[j], mi);
        mx = max(bc[j] - 1, mx);
      }
      int mip = i + 1;
      for (int j = L; j <= i; j++) {
        int y = p[j];
        if (bc[y] == mi) {
          mip = min(mip, j);
        }
      }
      vmi[L] = mi;
      vpmi[L] = mip;
      for (int j = L; j <= i; j++) {
        int y = p[j];
        if (bc[y] - 1 < mi) {
          vmi[j + 1] = bc[y] - 1; 
          vpmi[j + 1] = i + 1;
        } else {
          vmi[j + 1] = vmi[j];
          vpmi[j + 1] = vpmi[j];
        }
      }
      int mxp = i + 1;
      for (int j = i; j >= L; j--) {
        int y = p[j];
        if (bc[y] > mx) {
          mxp = j;
          mx = bc[y];
        }
        if (j <= R) {
          if (vmi[j] >= 0 && mx <= 2 * vmi[j] || (mx == 2 * vmi[j] + 1 && mxp <= vpmi[j])) {
            flg = true;
            break;
          }
        }
      }
      if (L > i && *max_element(bc.begin(), bc.end()) <= 2 * *min_element(bc.begin(), bc.end())) {
        flg = true;
      }
      if (flg) {
        cov = bcov;
        c = bc;  
        break;
      }
    }
  }
  assert(!*max_element(c.begin(), c.end()));
  assert(*min_element(cov.begin(), cov.end()));
  for (auto x : p) {
    printf("%d ", x + 1);
  }
  return 0;
}

Problem F Forbidden Tournament

  請先 get 一下前置結論

  先除去入度爲 0 的點。設剩下的點中標號最小的爲 $X$。

  因爲 $in(X)$ 不存在環,所以設 $P_i$ 是所有有到 $X$ 的邊的點,並且 $P_i$ 有邊到 $P_{i - 1}$。

  因爲 $out(X)$ 也不存在環,類似地定義 $Q_i$。

  設 $L = |in(X)|$

  不難發現和證明以下結論:

  • 存在邊 $Q_1 \rightarrow P_L$。
  • 如果存在邊 $Q_i \rightarrow P_j (i > 1)$,那麼存在邊 $Q_{i-1} \rightarrow P_j$
  • 如果存在邊 $Q_i \rightarrow P_j (j < L)$,那麼存在邊 $Q_i \rightarrow P_{j + 1}$
  • 上述條件是充分的

  然後枚舉一下 $|in(X)|, |out(X)|$,做個簡單 dp 就行了。

Code

#include <bits/stdc++.h>
using namespace std;

#ifdef local
#define _assert(expr) assert(expr)
#else
#define _assert(expr)
#endif

int Mod = 998244353;

typedef long long ll;

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}
int inv(int a, int Mod = ::Mod) {
  int x, y;
  exgcd(a, Mod, x, y);
  return (x < 0) ? (x + Mod) : x;
}

class Z {
  public:
    int v;

    Z() {	}
    Z(int v) : v(v) {
      _assert(v >= 0 && v < Mod);
    }
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (Z a, Z b) {
      int x;
      return Z((x = a.v + b.v) >= Mod ? x - Mod : x); 
    }
    friend Z operator - (Z a, Z b) {
      int x;
      return Z((x = a.v - b.v) < 0 ? x + Mod : x);
    }
    friend Z operator * (Z a, Z b) {
      return 1ll * a.v * b.v;
    }
    friend Z operator ~ (Z a) {
      _assert(a.v);
      return inv(a.v);
    }
    friend Z operator - (Z a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
};

typedef Z Zi;

Zi qpow(Zi a, int p) {
  if (p < 0)
    p += Mod - 1;
  Zi rt = 1;
  for ( ; p; p >>= 1, a *= a) {
    if (p & 1) {
      rt *= a;
    }
  }
  return rt;
}
Zi qpow(Zi a, ll p) {
  return qpow(a, (int) (p % (Mod - 1)));
}

const int N = 405;

int n, K;

vector<Zi> fac, _fac;

void init_fac(int n) {
  fac.resize(n + 1);
  _fac.resize(n + 1);
  fac[0] = 1;
  for (int i = 1; i <= n; i++) {
    fac[i] = fac[i - 1] * i;
  }
  _fac[n] = ~fac[n];
  for (int i = n; i; i--) {
    _fac[i - 1] = _fac[i] * i;
  }
}
Zi comb(int n, int m) {
  return n < m ? 0 : fac[n] * _fac[m] * _fac[n - m];
}

Zi f[205][205];

Zi calc(int n, int m, int bu, int bd) {
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < m; j++) {
      f[i][j] = 0;
    }
  }
  for (int j = 0; j < m; j++) {
    f[0][j] = (j <= bu && j >= bd);
  }
  for (int i = 1; i < n; i++) {
    Zi sum = 0;
    for (int j = 0; j < m && j <= bu + i; j++) {
      sum += f[i - 1][j];
      if (j >= bd + i) {
        f[i][j] = sum;
      }
    }
  }
  Zi sum = 0;
  for (int j = max(bd + n - 1, 0); j < m && j < bu + n; j++) {
    sum += f[n - 1][j];
  }
  for (int i = 1; i < n; i++) {
    if (i + bu >= m) {
      sum += f[i][m - 1];
    }
  }
  return sum;
}

int main() {
  scanf("%d%d%d", &n, &K, &Mod);
  init_fac(405);
  K = n - 1 - K;
  Zi ans = !K;
  for (int sl = max(K, 1); sl < n; sl++) {
    for (int sr = max(K, 1); sl + sr < n; sr++) {
      Zi tmp = calc(sr, sl, sl - K, K - sr); 
//      cerr << tmp.v << " ";
      tmp = tmp * ~Zi(sl + sr + 1);
      ans += tmp;
    }
  }
  ans *= fac[n];
  printf("%d\n", ans.v);
  return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章