2026-01-21~22 hetao1733837 的刷题笔记
01-21
LGP10441 [JOIST 2024] 乒乓球 / Table Tennis
原题链接:[JOIST 2024] 乒乓球 / Table Tennis
分析
刚才在机房说话声音有点大啊......呃......好像有点康了......bur,自己一个人在机房待习惯了......有点愧疚......
我会 O ( 2 n ( n + 1 ) 2 × n 3 ) O(2^\frac{n(n+1)}{2}\times n^3) O(22n(n+1)×n3) 做法💀
一个有向图,是不是只会产生 C n 3 C_{n}^{3} Cn3 个三元环?而三元环只可能有两种情况,如图:

而右边的情况是当且仅当有一条边向外连了两条出边。即对于点集 ( i , j , k ) (i,j,k) (i,j,k),若 i → j i\rightarrow j i→j 且 i → k i\rightarrow k i→k,则 ( i , j , k ) (i,j,k) (i,j,k) 不成三元环。
呃......我是不是可以向题解思路靠近?
哦,就是一个结论:竞赛图三元环数量只与每个点的入度序列有关 。
对于一张完全没有三元环的图,其入度序列必定是 { 0 , 1 , 2 , ... , n − 1 } \{0,1,2,\dots,n-1\} {0,1,2,...,n−1}。
如果我们做一些小操作,比如有一段入度为 { x , x + 1 , x + 2 } \{x,x+1,x+2\} {x,x+1,x+2},将其变成 { x + 1 , x + 1 , x + 1 } \{x+1,x+1,x+1\} {x+1,x+1,x+1} 就会产生一个三元环,那似乎构造就可以了。
至于操作,找到一个三元环数量为 m m m 的最小图......就是这个东西,然后把图补全。那个最小的图就是一个组合数,嗯......
怎么写的有点像 lym 前几天给我发的语音。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5005;
int q, n, m, d[N], ans[N][N];
int calc(int x){
int tmp = x * ((x - 1) / 2) * (x / 2);
return (tmp - x * (x - 1) * (x - 2) / 6) / 2;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> q;
while (q--){
cin >> n >> m;
if (m > calc(n)){
cout << "No" << '\n';
continue;
}
cout << "Yes" << '\n';
for (int i = 1; i <= n; i++){
d[i] = i - 1;
}
for (int i = 1; i <= n; i++){
if (m <= calc(i)){
m = calc(i) - m;
if (i % 2 == 1){
for (int j = 1; j <= i; j++){
d[j] = (i - 1) / 2;
}
}
else{
for (int j = 1; j <= i; j++){
d[j] = i / 2 - (j <= i / 2);
}
}
while (m){
for (int l = 1, r = 1; r < n; l = r + 1){
r = l;
while (r < i && d[r + 1] == d[r])
r++;
int tmp = r;
while (m > 0 && l < r){
m--;
d[l++]--;
d[r--]++;
}
r = tmp;
}
}
break;
}
}
sort(d + 1, d + n + 1);
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
ans[i][j] = 0;
}
}
for (int i = n; i >= 2; i--){
d[i] = i - 1 - d[i];
int l = i - 1, r = i - 1;
while (true){
l = r;
while (l > 1 && d[l] == d[l - 1])
l--;
for (int j = l; j <= r; j++){
if (d[i] > 0){
d[i]--;
d[j]--;
ans[j][i] = 1;
}
else{
ans[i][j] = 1;
}
}
if (l == 1)
break;
r = l - 1;
}
}
for (int i = 2; i <= n; i++){
for (int j = 1; j < i; j++){
cout << ans[i][j];
}
cout << '\n';
}
}
}
没懂......
感觉那个
cpp
int calc(int x){
int tmp = x * ((x - 1) / 2) * (x / 2);
return (tmp - x * (x - 1) * (x - 2) / 6) / 2;
}
很类似 hxf 容斥吧......细节的话......我也不会证明。
我再想想......
相当于说......一张有 x x x 个点的图,最多含有的三元环个数是
x × x − 1 2 × x 2 − x × ( x − 1 ) × ( x − 2 ) 6 2 \frac{x\times\frac{x-1}{2}\times\frac{x}{2}-\frac{x\times (x-1)\times(x-2)}{6}}{2} 2x×2x−1×2x−6x×(x−1)×(x−2)
啥?其中 C x 3 = x × ( x − 1 ) × ( x − 2 ) 6 C_x^3=\frac{x\times (x-1)\times(x-2)}{6} Cx3=6x×(x−1)×(x−2)。那个分母上的 2 是反过来的两种图吧......
也就是说, x × x − 1 2 × x 2 x\times\frac{x-1}{2}\times\frac{x}{2} x×2x−1×2x 是所有图的情况?不见得完全吧......
为啥没人来机房啊?马上上课了,咋还没人来?
C x x − 2 = x × x − 1 2 C_x^{x-2}=x\times\frac{x-1}{2} Cxx−2=x×2x−1,那那个 x 2 \frac{x}{2} 2x 是啥?
就是,我从所有x个点里挑出来两个,然后不管他是从谁到谁,然后再挑一个,这个控制一下方向,减去所有......怎么说,就是不合法?然后我们之前不是不管那个方向吗?再除个2,好像是这样的。
并非这样吧......

理论上这个做法假完了!所以,看 aoao 的!
原作者给出了一些解释......

其实我没看懂......留到下辈子吧......
01-20
晚上居然还要考物理......发现 BO 想拿省一也不容易,进队也很难......所以,一方面来说要加倍努力,另一方面是做好心理准备......而且发现了吗?大幅提升实力,OI 就是现在,BO 也是现在。OI 是因为暑假基本没啥空余时间了,BO 是因为下学期一来基本就是停课,很难啊......OI 时间也会受影响。两个竞赛冲队重心都在高二,但是,我真的还有时间吗?如果想要更高的荣誉,那么,所要经受的一定更多。上天把我送到了双竞,一定有他的道理,我必须去完成他!
LGP5239 回忆京都
原题链接:回忆京都
分析
式子这么简单?卧槽,你从杨辉三角的角度理解是对的!
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 19260817
using namespace std;
const int N = 1005;
int q, n, m;
int sum[N][N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> q;
for (int i = 1; i < N; i++){
for (int j = 1; j < N; j++){
sum[i][j] = (sum[i - 1][j - 1] + sum[i - 1][j] + i) % mod;
}
}
for (int cs = 1; cs <= q; cs++){
cin >> n >> m;
cout << sum[m][n] << '\n';
}
}
LGP2606 [ZJOI2010] 排列计数
原题链接:[ZJOI2010] 排列计数
分析
这个 hxf 容斥似乎不太行,因为显然 ∀ \forall ∀ 好做一点。咦, m m m 是质数,需要 Lucas \operatorname{Lucas} Lucas 定理。
咋推式子啊😫
巧妙地转换!把 ⌊ i 2 ⌋ \lfloor\frac{i}{2}\rfloor ⌊2i⌋ 转化为小根堆 !
那么,设 f i f_i fi 表示前 i i i 个不同的数,满足小根堆性质的排列数。
那么,讨论左右子树即可,确定左子树合法方案数 l l l,右子树 r r r。得出转移方程 f i = C i − 1 l × f l × f r f_i=C_{i-1}^{l}\times f_l\times f_r fi=Ci−1l×fl×fr。
拿一个 Lucas \operatorname{Lucas} Lucas 定理没了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, m;
int fac[N], inv[N], f[N];
int lg[N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res = res * a % m;
b >>= 1;
a = a * a % m;
}
return res;
}
int C(int a, int b){
if (b == 0)
return 1;
int c = C(a / m, b / m);
int d = a % m;
int e = b % m;
int x;
if (d < e)
x = 0;
else
x = fac[d] * inv[e] % m * inv[d - e] % m;
return c * x % m;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
fac[0] = 1;
lg[0] = -1;
for (int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i % m;
lg[i] = lg[i >> 1] + 1;
}
int tmp = min(m - 1, n);
inv[tmp] = qpow(fac[tmp], m - 2);
for (int i = tmp - 1; i >= 0; i--){
inv[i] = inv[i + 1] * (i + 1) % m;
}
f[1] = f[2] = 1;
f[3] = 2;
int l = 1, r = 1;
for (int i = 4; i <= n; i++){
if (i - (1 << lg[i]) + 1 <= (1 << lg[i] - 1))
l++;
else
r++;
f[i] = C(i - 1, l) * f[l] % m * f[r] % m;
}
cout << f[n];
}