写在前面
这套题一如既往的魔幻,值得喷的地方依旧很多。
第一题小清新思维题还好。
第二题最长公共子序列是个原题!
第三题是个爆搜大模拟,数据和题解还都是错的!
期望得分:\(100pts + 60pts + 40pts = 200pts\)
实际得分:\(30pts + 60pts + 40pts = 130pts\)
T1 答案没取模挂惨了/kk
T2 只会 \(O(nm)\) 做法,正解是 \(O(n^2)\)。
T3 在洛谷上能跑到 50pts,众所周知机房机子跑的慢。
补题通道:
T1 | T2 | T3 |
---|---|---|
U175818 road | U175823 lcs | U175820 cube |
T1
Solution
简述题意就是 \(n\) 个点 \(n\) 条边,然后对无向边重定向。
对于一个环,当重定向后仍是一个环时,仅有两种情况(所有边全朝一个方向)。
如果这个环只有 \(x\) 个点,那么这个环一共有 \(2^x - 2\) 种合法方案。
不在环上的边怎么都无所谓。
可以把每个边的方向看做 01 两种情况,那么设第 \(i\) 个环上有 \(x_i\) 个边(点),一共有 \(k\) 个环,不在环上的边有 \(y\) 条,那么答案为:
\[2^y \times \prod_{i=1}^k (2^{x_i}-2) \pmod {10^9+7} \]至于判断环……
我不会 dfs,也没用 tarjan,写的生成树 + 树剖。
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#define LL long long
#define orz cout << "lkp AK IOI!\n"
using namespace std;
const LL MAXN = 1e5 + 500;
const LL INF = 1e9 + 7;
const LL mod = 1e9 + 7;
struct edge {
LL to, nxt;
}e[MAXN << 1], E[MAXN];
LL head[MAXN], num_edge = 1, sc = 0;
LL n, m, Cnt = 0;
LL dep[MAXN], Fa[MAXN];
LL fath[MAXN], siz[MAXN], son[MAXN], top[MAXN];
bool vis[MAXN];
LL read() {
LL f = 0, s = 0;
char ch = getchar();
while(!isdigit(ch)) f = (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 3) + (s << 1) + ch - '0', ch = getchar();
return f ? -s : s;
}
LL find(LL x) { return Fa[x] == x ? x : Fa[x] = find(Fa[x]); }
void add_edge(LL from, LL to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
void dfs(LL u, LL fa) {
// cout<<u<<" "<<fa<<"\n";
vis[u] = true;
dep[u] = dep[fa] + 1, fath[u] = fa, siz[u] = 1;
for(LL i = head[u]; i; i = e[i].nxt) {
LL v = e[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
if(siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(LL u, LL tp) {
top[u] = tp;
if(son[u]) dfs2(son[u], tp);
for(LL i = head[u]; i; i = e[i].nxt) {
LL v = e[i].to;
if(v == fath[u] || v == son[u]) continue;
dfs2(v, v);
}
}
LL Get_LCA(LL x, LL y) {
while(top[x] != top[y]) dep[top[x]] < dep[top[y]] ? y = fath[top[y]] : x = fath[top[x]];
return dep[x] < dep[y] ? x : y;
}
LL Pow(LL x, LL p) {
LL res = 1;
while(p) {
if(p & 1) res = res * x % mod;
x = x * x % mod;
p >>= 1;
}
return res;
}
int main() {
// freopen("road.in","r",stdin);
// freopen("road.out","w",stdout);
n = read();
for(LL i = 1; i <= n; ++i) Fa[i] = i;
for(LL i = 1, x; i <= n; ++i) {
x = read();
LL uf = find(i), vf = find(x);
if(uf != vf) {
add_edge(i, x), add_edge(x, i);
Fa[uf] = vf;
} else {
E[++sc] = (edge){i, x};
}
}
// cout<<sc<<"\n";
for(LL i = 1; i <= n; ++i) {
if(!vis[i]) {
dfs(i, 0);
dfs2(i, i);
}
}
int Ans = 1, tot = n;
// for(int i = 1; i <= n; ++i) cout<<dep[i]<<" "; puts("");
for(LL i = 1; i <= sc; ++i) {
LL u = E[i].to, v = E[i].nxt;
if(dep[u] < dep[v]) swap(u, v);
int lca = Get_LCA(u, v);
LL len = dep[u] + dep[v] - 2 * dep[lca] + 1;
// cout << u << " " << v << " " << lca<<" " << len << "\n";
// cout << dep[u]<<" "<<dep[v]<<" "<<dep[lca]<<"\n";
Ans = Ans * (Pow(2, len) - 2) % mod;
tot -= len;
}
Ans = Ans * Pow(2, tot) % mod;
cout << Ans << "\n";
return 0;
}
/*
5
2 3 1 5 4
8
8 1 1 2 2 3 3 7
*/
T2
Solution
一直把 T2 当场最长公共上升子序列了,淦。
考虑朴素 DP,记 \(f(i,j)\) 表示 \(s1\) 匹配到了第 \(i\) 位,\(s2\) 匹配到了第 \(j\) 位,的最长公共子序列长度,那么我们有如下递推关系:
\[f(i,j) = \max \begin{cases} f(i-1,j) \\ f(i,j-1) \\ f(i-1,j-1)+1 & [s1_i = s2_j] \end{cases} \]该 DP 时间复杂度和空间复杂度均为 \(O(\left\vert s1 \right\vert\left\vert s2 \right\vert)\)。
由于 \(s2\) 较长,考虑改变 DP 的角度。
记 \(g(i,j)\) 表示 \(s1\) 匹配到了第 \(i\) 位,最长公共子序列长度为 \(j\) 时,\(s2\) 可能匹配到的最左下标,那么我们有如下递推关系:
其中,\(nxt(i,ch)\) 表示 \(s2\) 第 \(i\) 位后第一个字母 \(ch\) 出现的位置。
该 DP 的时间复杂度为 \(O(\left\vert s1\right\vert^ 2 )\),空间复杂度为 \(O(\left\vert s1 \right\vert^2 + \left\vert s2 \right\vert \times 26)\)。
Code
/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e6+500;
const int INF = 1e9+7;
const int mod = 1e9+7;
char s1[1010], s2[MAXN];
int n, m, Ans = 0;
int nxt[MAXN][27], lst[27];
int f[1010][1010];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
int main()
{
cin >> s1 + 1 >> s2 + 1;
n = strlen(s1 + 1), m = strlen(s2 + 1);
for(int i = 0; i < 26; ++i) lst[i] = m + 1;
for(int i = 0; i < 26; ++i) nxt[m+1][i] = m + 1;
for(int i = m; i >= 1; --i) {
memcpy(nxt[i], nxt[i + 1], sizeof nxt[i]);
nxt[i][s2[i] - 'a'] = i;
// lst[s2[i] - 'a'] = i;
// for(int j = 0; j < 26; ++j) {
// nxt[i][j] = lst[j];
// }
}
memset(f, 0x3f, sizeof f);
f[1][1] = nxt[1][s1[1] - 'a'];
for(int i = 0; i <= n; ++i) f[i][0] = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
if(f[i - 1][j - 1] <= m) f[i][j] = min(f[i - 1][j], nxt[f[i - 1][j - 1] + 1][s1[i] - 'a']);
else f[i][j] = f[i - 1][j];
if(f[i][j] <= m) Ans = max(Ans, j);
}
}
printf("%d", Ans);
return 0;
}
T3
Solution
按照题意模拟即可,搜的时候先搜字典序小的,记录一个 \(lst\) 存上一次操作类型。
题解说前九操作和后九种操作一样,加上它们不会更优,所以可以不考虑。那爆搜就完了啊!
但是样例显然可以 1 16,比一个 2 更优(题目自己说的)。
所以题解的思路是错的,然后跑出来的数据也是错的。
不过值得借鉴的一点是,题解用了一个很 nb 的置换群,我不会,但码量贼短。要知道我自己考场上用一个半小时打了 8KB。
Code
代码是正确题意的代码,但是复杂度是 \(O(18^7)\) 还带一个常数,我没有想到怎么剪枝。
题目数据还是错的,如果想通过本题只需打前九个操作的部分即可。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#define orz cout << "lkp AK IOI!\n"
using namespace std;
const int MAXN = 1e5 + 500;
const int INF = 1e9 + 7;
const int mod = 1e9 + 7;
struct node {
int c[26];
bool operator < (const node &b) const {
for(int i = 1; i <= 24; ++i) {
if(c[i] != b.c[i]) {
return c[i] < b.c[i];
}
}
return true;
}
}base;
int n;
int stc[100], sc = 0;
map<node, bool> Map;
int read() {
int f = 0, s = 0;
char ch = getchar();
while(!isdigit(ch)) f = (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 3) + (s << 1) + ch - '0', ch = getchar();
return f ? -s : s;
}
namespace Work {
node A1(node x) {
int res = x.c[2];
x.c[2] = x.c[1], x.c[1] = x.c[3], x.c[3] = x.c[4], x.c[4] = res;
int p1 = x.c[24], p2 = x.c[23];
x.c[24] = x.c[9], x.c[23] = x.c[10];
x.c[9] = x.c[5], x.c[10] = x.c[6];
x.c[5] = x.c[13], x.c[6] = x.c[14];
x.c[13] = p1, x.c[14] = p2;
return x;
}
node A2(node x) { x = A1(x), x = A1(x); return x; }
node A3(node x) { x = A1(x), x = A1(x), x = A1(x); return x; }
node B1(node x) {
int res = x.c[10];
x.c[10] = x.c[9], x.c[9] = x.c[11], x.c[11] = x.c[12], x.c[12] = res;
int p1 = x.c[1], p2 = x.c[3];
x.c[1] = x.c[21], x.c[3] = x.c[23];
x.c[21] = x.c[17], x.c[23] = x.c[19];
x.c[17] = x.c[5], x.c[19] = x.c[7];
x.c[5] = p1, x.c[7] = p2;
return x;
}
node B2(node x) { x = B1(x), x = B1(x); return x; }
node B3(node x) { x = B1(x), x = B1(x), x = B1(x); return x; }
node C1(node x) {
int res = x.c[5];
x.c[5] = x.c[7], x.c[7] = x.c[8], x.c[8] = x.c[6], x.c[6] = res;
int p1 = x.c[3], p2 = x.c[4];
x.c[3] = x.c[12], x.c[4] = x.c[10];
x.c[12] = x.c[18], x.c[10] = x.c[17];
x.c[18] = x.c[13], x.c[17] = x.c[15];
x.c[13] = p1, x.c[15] = p2;
return x;
}
node C2(node x) { x = C1(x), x = C1(x); return x; }
node C3(node x) { x = C1(x), x = C1(x), x = C1(x); return x; }
node D1(node x) {
int res = x.c[13];
x.c[13] = x.c[15], x.c[15] = x.c[16], x.c[16] = x.c[14], x.c[14] = res;
int p1 = x.c[4], p2 = x.c[2];
x.c[4] = x.c[8], x.c[2] = x.c[6];
x.c[8] = x.c[20], x.c[6] = x.c[18];
x.c[20] = x.c[24], x.c[18] = x.c[22];
x.c[24] = p1, x.c[22] = p2;
return x;
}
node D2(node x) { x = D1(x), x = D1(x); return x; }
node D3(node x) { x = D1(x), x = D1(x), x = D1(x); return x; }
node E1(node x) {
int res = x.c[21];
x.c[21] = x.c[23], x.c[23] = x.c[24], x.c[24] = x.c[22], x.c[22] = res;
int p1 = x.c[19], p2 = x.c[20];
x.c[19] = x.c[9], x.c[20] = x.c[11];
x.c[9] = x.c[2], x.c[11] = x.c[1];
x.c[2] = x.c[11], x.c[1] = x.c[14];
x.c[11] = p1, x.c[14] = p2;
return x;
}
node E2(node x) { x = E1(x), x = E1(x); return x; }
node E3(node x) { x = E1(x), x = E1(x), x = E1(x); return x; }
node F1(node x) {
int res = x.c[17];
x.c[17] = x.c[19], x.c[19] = x.c[20], x.c[20] = x.c[18], x.c[18] = res;
int p1 = x.c[7], p2 = x.c[8];
x.c[7] = x.c[11], x.c[8] = x.c[12];
x.c[11] = x.c[22], x.c[12] = x.c[21];
x.c[22] = x.c[15], x.c[21] = x.c[16];
x.c[15] = p1, x.c[16] = p2;
return x;
}
node F2(node x) { x = F1(x), x = F1(x); return x; }
node F3(node x) { x = F1(x), x = F1(x), x = F1(x); return x; }
}
using namespace Work;
bool Check(node x) {
for(int i = 4; i <= 24; i += 4) {
int p = i / 4;
if(x.c[i - 3] == x.c[i - 2] && x.c[i - 2] == x.c[i - 1] && x.c[i - 1] == x.c[i]) continue;
return false;
}
return true;
}
namespace Subtask1 {
void Solve() {
node x = base;
if(Check(A1(x))) puts("1");
else if(Check(A2(x))) puts("2");
else if(Check(A3(x))) puts("3");
else if(Check(B1(x))) puts("4");
else if(Check(B2(x))) puts("5");
else if(Check(B3(x))) puts("6");
else if(Check(C1(x))) puts("7");
else if(Check(C2(x))) puts("8");
else if(Check(C3(x))) puts("9");
else if(Check(D1(x))) puts("10");
else if(Check(D2(x))) puts("11");
else if(Check(D3(x))) puts("12");
else if(Check(E1(x))) puts("13");
else if(Check(E2(x))) puts("14");
else if(Check(E3(x))) puts("15");
else if(Check(F1(x))) puts("16");
else if(Check(F2(x))) puts("17");
else if(Check(F3(x))) puts("18");
else puts("-1");
}
}
bool Flag = false;
void dfs(int stp, node now_, int lst) {
// if(Map[now_]) {
// Map[now_] = true;
// return ;
// }
if(Check(now_)) {
Flag = true;
return ;
}
if(stp > n) return ;
if(lst != 1 && lst != 2 && lst != 3) { stc[++sc] = 1; dfs(stp + 1, A1(now_), 1); if(Flag) return ; sc--; }
if(lst != 1 && lst != 2 && lst != 3) { stc[++sc] = 2; dfs(stp + 1, A2(now_), 2); if(Flag) return ; sc--; }
if(lst != 1 && lst != 2 && lst != 3) { stc[++sc] = 3; dfs(stp + 1, A3(now_), 3); if(Flag) return ; sc--; }
if(lst != 4 && lst != 5 && lst != 6) { stc[++sc] = 4; dfs(stp + 1, B1(now_), 4); if(Flag) return ; sc--; }
if(lst != 4 && lst != 5 && lst != 6) { stc[++sc] = 5; dfs(stp + 1, B2(now_), 5); if(Flag) return ; sc--; }
if(lst != 4 && lst != 5 && lst != 6) { stc[++sc] = 6; dfs(stp + 1, B3(now_), 6); if(Flag) return ; sc--; }
// if(n > 3) return ;
if(lst != 7 && lst != 8 && lst != 9) { stc[++sc] = 7; dfs(stp + 1, C1(now_), 7); if(Flag) return ; sc--; }
if(lst != 7 && lst != 8 && lst != 9) { stc[++sc] = 8; dfs(stp + 1, C2(now_), 8); if(Flag) return ; sc--; }
if(lst != 7 && lst != 8 && lst != 9) { stc[++sc] = 9; dfs(stp + 1, C3(now_), 9); if(Flag) return ; sc--; }
if(lst != 10 && lst != 11 && lst != 12) { stc[++sc] = 10; dfs(stp + 1, D1(now_), 10); if(Flag) return ; sc--; }
if(lst != 10 && lst != 11 && lst != 12) { stc[++sc] = 11; dfs(stp + 1, D2(now_), 11); if(Flag) return ; sc--; }
if(lst != 10 && lst != 11 && lst != 12) { stc[++sc] = 12; dfs(stp + 1, D3(now_), 12); if(Flag) return ; sc--; }
if(lst != 13 && lst != 14 && lst != 15) { stc[++sc] = 13; dfs(stp + 1, E1(now_), 13); if(Flag) return ; sc--; }
if(lst != 13 && lst != 14 && lst != 15) { stc[++sc] = 14; dfs(stp + 1, E2(now_), 14); if(Flag) return ; sc--; }
if(lst != 13 && lst != 14 && lst != 15) { stc[++sc] = 15; dfs(stp + 1, E3(now_), 15); if(Flag) return ; sc--; }
if(lst != 16 && lst != 17 && lst != 18) { stc[++sc] = 16; dfs(stp + 1, F1(now_), 16); if(Flag) return ; sc--; }
if(lst != 16 && lst != 17 && lst != 18) { stc[++sc] = 17; dfs(stp + 1, F2(now_), 17); if(Flag) return ; sc--; }
if(lst != 16 && lst != 17 && lst != 18) { stc[++sc] = 18; dfs(stp + 1, F3(now_), 18); if(Flag) return ; sc--; }
}
int main() {
// freopen("cube.in","r",stdin);
// freopen("cube.out","w",stdout);
n = read();
for(int i = 1; i <= 24; ++i) {
base.c[i] = read();
}
if(n == 1) { Subtask1::Solve(); }
else {
dfs(1, base, 0);
for(int i = 1; i <= sc; ++i) printf("%d ", stc[i]);
puts("");
}
return 0;
}
/*
2
1 1 1 1
4 4 2 2
6 6 3 3
3 3 6 6
5 5 5 5
4 4 2 2
*/