20210823模拟赛解题报告

写在前面

这套题一如既往的魔幻,值得喷的地方依旧很多。

第一题小清新思维题还好。

第二题最长公共子序列是个原题!

第三题是个爆搜大模拟,数据和题解还都是错的!

期望得分:\(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\) 可能匹配到的最左下标,那么我们有如下递推关系:

\[g(i,j) = \max \begin{cases} g(i-1,j) \\ nxt(g(i-1,j-1),s1_i) \end{cases} \]

其中,\(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
*/
posted @ 2021-08-23 10:25  Suzt_ilymtics  阅读(58)  评论(0编辑  收藏  举报