每次打牛客 Debuff 拉满, 受不了。

K

签到题。

F

签到题。

L

简单题。
因为 $n$ 很小, 考虑直接暴力枚举两个颜色是否有 $i > j$, 如果有就连边 $j \rightarrow i$ 。把 $1, 2$ 看成同一个点跑拓扑求最长路即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
#define rint register int
using namespace std;

const int N=1005;
int n,r[N],g[N],b[N],rd[N],ans[N],vis[N][N];
vector<int> e[N];
queue<int> q;

void Add(rint x,rint y){
if(x==2) x=1;
if(y==2) y=1;
if(vis[x][y]) return;
e[x].push_back(y);
rd[y]++;
vis[x][y]=1;
}

int main(){
scanf("%d",&n);
for(rint i=1;i<=n;i++){
scanf("%d%d%d",&r[i],&g[i],&b[i]);
ans[i]=-1;
for(rint j=1;j<i;j++){
if(r[j]<r[i]&&g[j]<g[i]&&b[j]<b[i]){
Add(j,i);
}
if(r[j]>r[i]&&g[j]>g[i]&&b[j]>b[i]){
Add(i,j);
}
}
}
for(rint i=1;i<=n;i++){
if(i==2) continue;
if(!rd[i]){
ans[i]=0;
q.push(i);
}
}
while(!q.empty()){
int u=q.front();
q.pop();
for(rint i=0;i<e[u].size();i++){
int v=e[u][i];
ans[v]=max(ans[v],ans[u]+1);
--rd[v];
if(!rd[v]) q.push(v);
}
}
rint flag=0;
if(ans[1]==-1||vis[1][1]) flag=1;
for(rint i=1;i<=n;i++){
if(i==2) continue;
if(ans[i]>255||ans[i]==-1) flag=1;
}
if(flag) puts("-1");
else{
printf("%d\n%d\n",ans[1],ans[1]);
for(rint i=3;i<=n;i++) printf("%d\n",ans[i]);
}
return 0;
}

F

显然需要状压, 但是因为我是sb总觉得要把选了哪些数状压下来, 但是因为每个数合法区间是一个后缀, 所以完全不需要, 只需要记录选了几个就知道当前位置填法。
$f (i, j, s) $ 表示前 $i$ 个位置选了 $j$ 个数, 后 $k - 1$ 个的情况是 $s$ 即可。
注意 $k = 1$ 的时候状压里面要特判。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <bits/stdc++.h>

#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
#define debug(...) fprintf (stderr, __VA_ARGS__)

using std :: cin;
using std :: cout;
using std :: endl;
using std :: cerr;
using i64 = long long;

const int P = 1e9 + 7;
inline int mod(int x) { return x + (x >> 31 & P); }
inline void sub(int &x, int y) { x = mod(x - y); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline int add(int x, int y) { return mod(x + y - P); }
inline int dec(int x, int y) { return mod(x - y); }
inline int power(int x, int k) {
int res = 1; if (k < 0) k += P - 1;
while (k) { if (k & 1) res = 1ll * res * x % P; x = 1ll * x * x % P; k >>= 1; }
return res;
}

const int N = 1e6 + 6;

int n, t, k, m;
int f[15][1 << 12], g[15][1 << 12], count[1 << 12];
int x[15];
int fac[N];
int cnt[305];

void solve() {
cin >> n >> t >> k >> m;
lep (i, 0, n - 1) cin >> x[i], cnt[x[i]] ++;

int mx = (1 << (k - 1)) - 1;

for (int i = 1; i <= (1 << (k - 1)); i ++)
count[i] = count[i - (i & -i)] + 1;

fac[0] = 1;
lep (i, 1, 20) fac[i] = 1ll * fac[i - 1] * i % P;

f[0][0] = 1;
int now = 0;
lep (i, 1, t) {
memset (g, 0, sizeof (g));
now += cnt[i];
lep (j, 0, n)
lep (s1, 0, (1 << (k - 1)) - 1) if (f[j][s1]) {
pls (g[j][(s1 << 1) & mx], f[j][s1]);
if (count [s1] < m)
pls (g[j + 1][(((s1 << 1) & mx) | 1) & mx], 1ll * f[j][s1] * (now - j) % P);
}
std :: swap (f, g);
}
int ans = 0;
rep (s2, (1 << (k - 1)) - 1, 0) pls (ans, f[n][s2]);
cout << ans << endl;
}

int main() {
std :: ios :: sync_with_stdio(false);
int Case = 1;
while (Case --) solve();
return 0;
}

D

首先行是独立的所以可求每一行的 SG 然后异或起来。
首先 $m$ 是偶数的时候只和 $1$ 个数有关, SG值只能是 $0/1$ 。
然后是 $m$ 奇数的情况, 队友打表给过了, 我想的时候错误认为左右两边只有一边有的时候 SG 是2, 但是显然是不正确的。考虑到左右两边是否存在 $1$ 影响到中间这个点能不能选, 我们讨论左右两边是否有积木。难以意识到的是, 两边有一个积木和有多个积木效果并不相同, 我在打表中才意识到这个问题并分开讨论。

  • 如果正中间不是 $1$ 和偶数一样。
  • 如果中间是 $1$ 如果左右两边有一边是没有的, 那就只能一边拿到没。也正是因为这使得中间的 $1$ 不能被取走, 所以两边个数为 $1$ 和大于 $1$ 是不同的。
  • 如果左右两边都有, 且有一边只有一个, 那么把中间拿了和把只有 $1$ 个那边拿了, 一定会产生 $0, 1$ 两种SG中的一个, 只需要考虑拿大于一个的那边, 容易发现和奇偶性相关, 如果有 $x$ 个, 那 SG值 就是 $2 + (x + 1) \pmod 2$ 。
  • 如果左右都大于一个, 因为中间点迟早要决定, 所以提前决定中间点讨论是否选择中间点可以知道有奇数个 $1$ 则先手必败, 反之是后手。 SG值也只和奇偶性有关。

异或所有行的SG即可。

J

显然找一个环即可。
建一个反图找终止点可以到哪些点, 正图上dfs找环就行。
注意 dfs 生成树在有向图上是有横叉边的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <bits/stdc++.h>

#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
#define debug(...) fprintf (stderr, __VA_ARGS__)

using std :: cin;
using std :: cout;
using std :: endl;
using std :: cerr;
using i64 = long long;

const int P = 1e9 + 7;
inline int mod(int x) { return x + (x >> 31 & P); }
inline void sub(int &x, int y) { x = mod(x - y); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline int add(int x, int y) { return mod(x + y - P); }
inline int dec(int x, int y) { return mod(x - y); }
inline int power(int x, int k) {
int res = 1; if (k < 0) k += P - 1;
while (k) { if (k & 1) res = 1ll * res * x % P; x = 1ll * x * x % P; k >>= 1; }
return res;
}

const int N = 2e6 + 5;

int n, m, k;
int endpos[N];
std :: vector<std :: pair<int, char> > e[N], g[N];

int vis[N], bj[N];

void dfs1 (int x) {
if (vis[x]) return ;
vis[x] = 1;
for (auto [y, c] : g[x]) dfs1 (y);
}

std :: vector<char> stk;
int dep[N], flg = 0;

int nowend;
std :: string nowans;
int in[N];

void dfs2 (int x, int mxd, int mxp) {
if (flg) return ;
// if (bj[x]) return ;
bj[x] = 1; in[x] = 1;

for (auto [y, c] : e[x]) {
if (flg) return ;
if (! bj[y]) dep[y] = dep[x] + 1,stk.push_back (c), dfs2 (y, vis[y] ? dep[y] : mxd, vis[y] ? y : mxp), stk.pop_back();
else {
if (dep[y] <= mxd && in[y]) {
//cerr << dep[y] << ' ' << mxd << ' ' << mxp << endl;
flg = 1;
stk.push_back (c);
for (int i = dep[y]; i <= mxd - 1; i ++)
stk.push_back (stk[i]);
nowend = mxp;
for (int i = 0; i < stk.size(); i ++) nowans += stk[i];
return ;
}
}
}
in[x] = 0;
}

std :: vector<char> rec;

void dfs3 (int x) {
if (vis[x]) return ;
vis[x] = 1;
if (x == nowend) {
std :: reverse(rec.begin(), rec.end());
cout << nowans;
for (char ch : rec) cout << ch;
cout << endl;
exit (0);
}
for (auto [y, ch] : g[x]) rec.push_back (ch), dfs3 (y), rec.pop_back();
}

int main() {
std :: ios :: sync_with_stdio(false);
cin >> n >> m >> k;
lep (i, 1, k) {
int x;
cin >> x;
endpos[x] = 1;
}
lep (i, 1, m) {
int x, y; char c;
cin >> x >> y >> c;
e[x].push_back ( {y, c} );
g[y].push_back ( {x, c} );
}

lep (i, 1, n) if (endpos[i] && ! vis[i]) {
dfs1 (i);
}

dfs2 (1, vis[1] ? 0 : (int) - 1e9, vis[1] ? 1 : 0);

if (flg == 0) return cout << -1 << endl, 0;

//cerr << nowend << endl;
//cout << nowans << endl;

memset (vis, 0, sizeof (vis));

lep (i, 1, n) if (endpos[i] && ! vis[i]) {
dfs3 (i);
}
assert (0);
return 0;
}

H

考试的时候发现这个东西很像以前玩GF的时候求导的东西, 然后搞出来式子以后发现是某个 MIT 课程的课后作业, 遂抄之, 然后因为不懂双曲正弦余弦函数暴毙(

递推做法是啥啊没懂啊(