多校训练营9

E:Longest Increasing Subsequence

LyFive

标签

构造

题意

构造一个排列,使得这个排列的最长上升子序列的数量有个。

思路

首先能够发现能够构造所有2的次幂且最长上升子序列长度为,不难发现该序列的前缀序列就能分别构造得到,将转换成2进制的形式,分别在前面加入数字就能构造出对应。但加入的新数字并不能成为最长子序列,原因是插入在前保证前面的最长上升子序列长度为包括自己为,因此还需在后面加入个数字才能保证,因此不难发现对于后面含有1个位已经填入,缺少的是0的位。因此只需要考虑,对于每一位1后加入等量0的数字,并保证新插入的数完全是递增即可。

代码
参考代码
 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
#include <bits/stdc++.h>
using namespace std;

const LL N = 110;

int a[N];
int n, m, x, y, k, t, q, c;

void init()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
}

void insert(int p, int num)
{
    for (int i = c; i >= p; i--)
    {
        a[i] = a[i - 1];
    }
    a[p] = num;
    y++, c++;
}
void solve()
{
    cin >> m;
    if (m == 1)
    {
        cout << 1 << "\n" << 1 << "\n";
        return;
    }
    x = m;
    k = -1;
    while (x)
    {
        x /= 2;
        k++;
    }
    for (int i = 0; i < 2 * k; i += 2)
    {
        a[i] = i + 2;
        a[i + 1] = i + 1;
    }
    c = 2 * k;
    x = m;
    y = 2 * k + 1;
    t = 0;
    int pos = 0, f = 0;
    for (int i = 0; i < k; i++)
    {
        t++;
        if (x % 2)
        {
            while (a[pos] != 2 * t)
                pos++;
            insert(pos, y);
            f = 1;
        }
        else if (f)
        {
            pos++;
            insert(pos, y);
        }
        x /= 2;
    }
    cout << c << "\n";
    for (int i = 0; i < c; i++)
    {
        cout << a[i] << " ";
    }
    cout << "\n";
}

int main()
{
    init();
    int t;
    cin >> t;
    for (int i = 0; i < t; i++)
    {
        solve();
    }
}

G:Magic Spells

LyFive

标签

Manacher;字符串hash;PAM(回文自动机)

题意

给定个字符串,统计其相同回文子串的数量。

思路

可以简单推理得到,对于任意一个字符串其中本质不同的回文字串个数不超过。 方案一:由于很小,我们可以利用遍历判断得到所有回文子串,保留他们的值。最终判断值出现的次数统计即可。 方案二:对于个字符串分别构造,并对自动机进行遍历及回溯,每一个状态代表一个本质不同的回文子串,最终遍历所有状态进行统计即可。(参考代码采用方案二)

代码
参考代码
  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
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300000 + 5;
int k;
struct PAM
{
    int sz, tot, last;
    int cnt[maxn], ch[maxn][26], len[maxn], fail[maxn];
    char s[maxn];

    int node(int l)
    { // 建立一个新节点,长度为 l
        sz++;
        memset(ch[sz], 0, sizeof(ch[sz]));
        len[sz] = l;
        fail[sz] = cnt[sz] = 0;
        return sz;
    }

    void clear()
    { // 初始化
        sz = -1;
        last = 0;
        s[tot = 0] = '$';
        node(0);
        node(-1);
        fail[0] = 1;
    }

    int getfail(int x)
    { // 找后缀回文
        while (s[tot - len[x] - 1] != s[tot])
            x = fail[x];
        return x;
    }

    void insert(char c)
    { // 建树
        s[++tot] = c;
        int now = getfail(last);
        if (!ch[now][c - 'a'])
        {
            int x = node(len[now] + 2);
            fail[x] = ch[getfail(fail[now])][c - 'a'];
            ch[now][c - 'a'] = x;
        }
        last = ch[now][c - 'a'];
        cnt[last]++;
    }
} pam[5];
char s[maxn];

int p[5] = {0, 0, 0, 0, 0};
int ans;

bool st[maxn];

void dfs()
{
    for (char a = 0; a < 26; a++)
    {
        int mp[5] = {1, 1, 1, 1, 1};
        memcpy(mp, p, sizeof p);
        bool f = false;
        for (int i = 0; i < k; i++)
        {
            if (pam[i].ch[p[i]][a])
            {
                p[i] = pam[i].ch[p[i]][a];
            }
            else
            {
                f = true;
                break;
            }
        }
        if (!f && !st[p[0]])
        {
            ans++;
            st[p[0]] = 1;
            dfs();
        }
        memcpy(p, mp, sizeof p);
    }
    if (p[0] != 1)
    {
        for (int i = 0; i < k; i++)
        {
            p[i] = pam[i].fail[p[i]];
        }
        if(!st[p[0]])
            dfs();
    }
}
int main()
{
    cin >> k;
    for (int x = 0; x < k; x++)
    {
        scanf("%s", s + 1);
        pam[x].clear();
        for (int i = 1; s[i]; i++)
        {
            pam[x].insert(s[i]);
        }
    }
    dfs();
    cout << ans << endl;
    return 0;
}

I:The Great Wall II

z3475

标签

单调队列

题意

给定长度为的数列,可以将其分割为段连续的子串,其代价是每段的最大值。求分割成段代价的最小值。

思路
代码
参考代码
 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
#include <bits/stdc++.h>
using namespace std;
struct FAST_IO {
  FAST_IO() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
  }
} __fast_io;
template <class T1, class T2>
bool cmin(T1& a, const T2 b) {
  return a > b ? a = b, 1 : 0;
}

bool MULTIDATA = true;

int n, ci;
array<int, 3> c[8011];
int a[8011], b[8011], DP[8011][8011];
int main() {
  cin >> n;
  for (int i = 1; i <= n; i++)
    cin >> a[i];
  a[0] = 0x3f3f3f3f;
  memset(DP, 0x3f, sizeof(DP));
  DP[0][0] = 0;
  for (int k = 1; k <= n; k++) {
    ci = 0;
    if (k == 1)
      c[ci++] = {a[1], DP[k - 1][0],DP[k - 1][0]+a[1]};
    for (int i = 1; i <= n; i++) {
      if (ci){
        DP[k][i] = c[ci-1][2];
      }
      if (i != n) {
        if (ci && c[ci - 1][0] <= a[i + 1]) {
          int f = ci - 1;
          while (f != 0 && c[f - 1][0] <= a[i + 1])
            f--;
          c[f] = *min_element(&c[f], &c[ci],
                              [](auto&& a, auto&& b) { return a[1] < b[1]; });
          c[f][0] = a[i + 1];
          c[f][2] = c[f][0]+c[f][1];
          if (f!=0)
            cmin(c[f][2],c[f-1][2]);
          ci = f + 1;
        }
        if (DP[k - 1][i] != 0x3f3f3f3f){
          c[ci] = {a[i + 1], DP[k - 1][i],a[i + 1]+DP[k - 1][i]};
          if (ci!=0){
            cmin(c[ci][2],c[ci-1][2]);
          }
          ci++;
        }
      }
    }
    cout << DP[k][n] << endl;
  }
}

评论