Loading

「解题报告」2023-10-17 模拟赛

Prufer 序列(prufer)

题目描述:

Pigbrain 不知道什么时候学习了 \(\texttt{prufer}\) 序列。

\(\texttt{prufer}\) 序列可以用来表示一棵树,其构造方法是这样的:

对于给定的树,假设节点编号为 \(1 \dots n\),那么进行这样的操作:

  1. 找到编号最小的度数为 \(1\) 的点。

  2. 删除该节点,并在序列末尾添加与该节点相邻的点的编号。

  3. 重复 \(1、2\) 操作,直到树中只剩下两个节点。

容易知道对于一棵 \(n\) 个点的树,这样得到的序列的长度为 \(n−2\)

输入描述:

\(1\) 行一个整数 \(𝑛\),表示树中节点的个数。

\(2 \dots 𝑛\) 行,每行两个整数 \(u, v\),表示树上的一条边。保
证给定的是一棵树。

输出描述:

一行 \(n−2\) 个整数,代表树的 \(\texttt{prufer}\) 序列。

输入样例#1:

6 
1 3 
1 5 
3 2 
3 6 
5 4 

输出样例#1:

3 5 1 3 

样例#1 解释:

参考题目描述。

输入/输出样例#2、#3:

见下发文件。

数据范围:

对于 \(30\%\) 的数据,树是一条链;

对于另外 \(10\%\) 的数据,满足输入的 \(u = 1\)

对于另外 \(50\%\) 的数据,\(n \le 3000\)

对于 \(100\%\) 的数据,\(n \le 2×10^5\)

用小根堆来维护最小编号即可。

// The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

template<typename T>
void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

template<typename T>
void print(T x, char c) {
   write(x);
   putchar(c);
}

const int N = 2e5 + 5;

int n, cnt;
int dug[N];
bool ch[N];
vector<int> e[N], ans;
priority_queue<int, vector<int>, greater<int> > q;

int main() {
    // freopen("prufer.in", "r", stdin);
    // freopen("prufer.out", "w", stdout);
    n = read<int>();
    int x, y;
    rep (i, 1, n - 1, 1) {
        x = read<int>(), y = read<int>();
        e[x].emplace_back(y);
        e[y].emplace_back(x);
        ++ dug[x];
        ++ dug[y];
    }
    rep (i, 1, n, 1) {
        ch[i] = 1;
        if (dug[i] == 1) {
            q.emplace(i);
        }
    }
    int u;
    while (cnt < n - 2) {
        u = q.top();
        q.pop();
        for (int v : e[u]) {
            -- dug[v];
            if (dug[v] == 1 && ch[v]) {
                q.emplace(v);
            }
            if (ch[v]) {
                ans.emplace_back(v);
            }
        }
        ch[u] = 0;
        ++ cnt;
    }
    for (int v : ans) {
        print(v, ' ');
    }
    return 0;
}

美丽括号(beauty)

题目描述:

Pigbrain 有着独特的审美方式。

现在 Pigbrain 手上有一个长度为 \(n\) 的括号序列。根据 Pigbrain 的审美方式,他认为一个括号序列是美丽的,当且仅当:

  1. 括号序列的长度为偶数

  2. 括号序列的前半部分是左括号,后半部分是右括号。

现在,Pigbrain 想要删去原括号序列中的一部分括号(可以一个都不删除),使得剩下的括号组成一个美丽的括号序列。Pigbrain 当然会做啦,但是他想知道有多少种方法。你能帮他解决这个问题吗?

两种方法被认为是不同的,当且仅当存在一个位置,使得在其中一种方法中该位置的元素被删除,而另一个方
法中没有删除。

注意,尽管你的审美可能与 Pigbrain 不同,但你依然要帮他解决这个问题。

输入描述:

一行一个字符串,表示 Pigbrain 的括号序列。

输出描述:

一行,一个整数,代表答案。因为答案可能很大,因
此请输出答案对 \(10^9 +7\) 取模的结果。

输入样例#1:

()()() 

输出样例#1:

7 

样例#1 解释:

删除后得到 () 的方法有 \(6\) 种,得到 (()) 的有 \(1\) 种,因此答案为 \(6+1=7\)

输入样例#2:

((()))

输出样例#2:

19 

样例#2 解释:

删除后得到 () 的方法有 \(3×3=9\) 种,删除后得到 (()) 的方法有 \(\dfrac{3×2}{2} \times \dfrac{3×2}{2} = 9\) 种,删除后得到 ((())) 的方法有 \(1\) 种。

输入样例#3:

)( 

输出样例#3:

0 

样例#3 解释:

并没有合法的方案。

20 分做法

直接 dfs 搜索即可

50 分做法

枚举每一个左括号,枚举美丽括号的长度,设这个左括号为最右边的左括号,\(n\) 为该左括号左边的左括号的数量,\(m\) 为该左括号右边的右括号的数量,则答案为 \(\sum_{i = 1}^{\min \left (n, m \right )} C_{n - 1}^{i - 1} \times C_{m}^{i}\)

70 分做法

特判特殊性质即可。

// The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

template<typename T>
void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

template<typename T>
void print(T x, char c) {
   write(x);
   putchar(c);
}

const int N = 2e5 + 5;
const int mod = 1e9 + 7;

int n, cnt, cntleft, cntright;
ll ans;
char s[N];
bool yes[N];
ll cntl[N], cntr[N];
ll fac[N], inv[N];

void work() {
    int cot = 0;
    rep (i, 0, n - 1, 1) {
        if (yes[i]) {
            ++ cot;
            if (cot <= cnt / 2 && s[i] != '(') {
                return ;
            }
            if (cot > cnt / 2 && s[i] != ')') {
                return ;
            }
        }
    }
    ++ ans;
    ans %= mod;
}

void dfs(int u) {
    if (u == n) {
        if (cnt & 1 || cnt == 0) {
            return ;
        }
        work();
        return ;
    }
    yes[u] = 1;
    ++ cnt;
    dfs(u + 1);
    yes[u] = 0;
    -- cnt;
    dfs(u + 1);
}

ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) {
            res = res * x % mod;
        }
        y >>= 1;
        x = x * x % mod;
    }
    return res;
}

ll C(ll n, ll m) {
    if (n < m)  return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int main() {
    // freopen("beauty.in", "r", stdin);
    // freopen("beauty.out", "w", stdout);
    scanf("%s", s);
    n = strlen(s);
    if (n <= 20) {
        dfs(0);
        print(ans, '\n');
        return 0;
    }
    fac[0] = inv[0] = 1;
    rep (i, 1, n + 2, 1) {
        fac[i] = fac[i - 1] * i % mod;
    }
    inv[n + 2] = qpow(fac[n + 2], mod - 2);
    per (i, n + 1, 1, 1) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    int x = 0, fg = 1;
    rep (i, 0, n - 2, 1) {
        if (s[i] != s[i + 1]) {
            if (x) {
                fg = 0;
                break ;
            } else {
                x = i + 1;
            }
        }
    }
    if (fg) {
        int lim = min(x, n - x);
        rep (i, 1, lim, 1) {
            ans = (ans + C(x, i) * C(n - x, i) % mod) % mod;
        }
        print(ans, '\n');
        return 0;
    }
    rep (i, 1, n, 1) {
        if (s[i] == '(') {
            ++ cntleft;
        } else {
            ++ cntright;
        }
    }
    rep (i, 1, n - 1, 1) {
        if (s[i - 1] == '(') {
            cntl[i] = (cntl[i - 1] + 1) % mod;
        } else {
            cntl[i] = cntl[i - 1];
        }
    }
    per (i, n - 2, 0, 1) {
        if (s[i + 1] == ')') {
            cntr[i] = (cntr[i + 1] + 1) % mod;
        } else {
            cntr[i] = cntr[i + 1];
        }
    }
    ll lim = min(cntleft, cntright);
    ll res = 0;
    rep (i, 1, lim, 1) {
        res = 0;
        rep (j, 0, n - 1, 1) {
            if (s[j] == '(') {
                res = (res + 1ll * C(cntl[j], i - 1) % mod * C(cntr[j], i) % mod) % mod;
            }
        }
        ans = (ans + res) % mod;
    }
    print(ans, '\n');
    return 0;
}

100 分做法

上面的式子 \(\sum_{i = 1}^{\min \left (n, m \right )} C_{n - 1}^{i - 1} \times C_{m}^{i}\),设 \(n \le m\),那么这个式子可以写成 \(\sum_{i = 1}^{n} C_{n - 1}^{n - i} \times C_{m}^{i}\),由于 \(C_{n - 1}^{n} = 0\),所以这个式子可以再扩展为 \(\sum_{i = 0}^{n} C_{n - 1}^{n - i} \times C_{m}^{i}\),这个式子等于 \(\sum_{i = 1}^{n}C_{n + m - 1}^{n}\),复杂度 \(O(n)\)

// The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

template<typename T>
void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

template<typename T>
void print(T x, char c) {
   write(x);
   putchar(c);
}

const int N = 2e5 + 5;
const int mod = 1e9 + 7;

int n;
ll ans;
char s[N];
ll cntr[N];
ll fac[N << 1], inv[N << 1];

ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) {
            res = res * x % mod;
        }
        y >>= 1;
        x = x * x % mod;
    }
    return res;
}

ll C(ll n, ll m) {
    if (n < m)  return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int main() {
    scanf("%s", s);
    n = strlen(s);
    fac[0] = inv[0] = 1;
    rep (i, 1, N - 1, 1) {
        fac[i] = fac[i - 1] * i % mod;
    }
    inv[N - 1] = qpow(fac[N - 1], mod - 2);
    per (i, N - 2, 1, 1) {
        inv[i] = inv[i + 1] * (i + 1) % mod;
    }
    per (i, n - 1, 0, 1) {
        if (s[i] == ')') {
            cntr[i] = (cntr[i + 1] + 1) % mod;
        } else {
            cntr[i] = cntr[i + 1];
        }
    }
    int cnt = 0;
    rep (i, 0, n - 1, 1) {
        if(s[i] == '(') {
			++ cnt;
			ans = (ans + C(cnt - 1 + cntr[i], cnt)) % mod;
		}
    }
	print(ans, '\n');
    return 0;
}

字串谜题(string)

题目描述:

Pigbrain 将美丽的括号序列种了下去,希望能够得到
更多的括号序列。

然而,地里长出了一棵树。

这棵树很奇怪。除了树根以外,它有 \(n\) 个节点。其中每个节点要么是从根部长出来,要么是接在其他节点上。

Pigbrain 仔细观察了这棵树,发现这棵树的每条树枝,包括从根部连出来的树枝上,都写着一个小写字母。

不难发现,这里的每个节点都可以表示一个字符串:将从根部到该节点的路径上所经过的树枝上的字母依次写下,就能得到一个字符串。

Pigbrain 发现这件事之后,想要进行 \(m\) 次询问。每次 Pigbrain 会给定一个字符串 \(S\) 和一个数字 \(x\),他想要知道 \(S\) 在第 \(x\) 个节点表示的字符串中出现了多少次。你能帮帮他吗?

输入描述:

\(1\) 行输入一个整数:\(n\),代表除根以外的节点个数。

接下来的 \(n\) 行,第 \(i\) 行的输入会是以下两种形式中的一种:

\(1 \ c\) —— 代表第 \(i\) 个节点是从根部直接长出来的,对应的树枝上写着字母 \(c\)

\(2 \ j \ c\) —— 代表第𝑖个节点是从节点 \(j(j<i)\) 长出来的,对应的树枝上写着字母 \(c\)

下一行输入一个整数:\(m\)

接下来的 \(m\) 行,每行输入格式为:

\(x \ S\)——代表询问字符串 \(S\) 在第 \(x\) 个节点表示的字符串中出现了多少次。

输出描述:

对于每个询问,输出一行,代表答案。

输入样例#1:

20 
1 d 
2 1 a 
2 2 d 
2 3 a 
2 4 d 
2 5 a 
2 6 d 
2 7 a 
1 d 
2 9 o 
2 10 k 
2 11 i 
2 12 d 
2 13 o 
2 14 k 
2 15 i 
2 1 o 
2 17 k 
2 18 i 
2 15 i 
12 
8 da 
8 dada 
8 ada 
6 dada 
3 dada 
19 doki 
19 ok 
16 doki 
15 doki 
9 d 
1 a 
20 doki

输出样例#1:

4 
3 
3 
2 
0 
1 
1 
2 
1 
1 
0 
2

数据范围:

对于 \(20\%\) 的数据,\(n \le 500,|S| \le 500,\sum|S| \le 1000\)

对于 \(38\%\) 的数据,\(n \le 3000,m \le 3000,|S| \le 3000\)

对于另外 \(14\%\) 的数据,第 \(i\) 个节点从第 \(i-1\) 个节点长
出。特别地,第 \(1\) 个节点直接接在树根。

对于另外 \(14\%\) 的数据,所有询问中的 \(S\) 都相等。

对于另外 \(14\%\) 的数据,所有询问中的 \(x\) 都相等。

对于 \(100\%\) 的数据,满足 \( 1 \le n \le 4 \times 10^5,1 \le m \le 4 \times 10^5 1 \times x \times 𝑛,|S| \le 4 \times 10^5,\sum|S| \le 4 \times 10^5\)

数据范围中的 \(S\) 均指询问中给出的字符串。

保证给出的树中只含小写字母。

提示:

下发样例#2 满足\(𝑛 \le 500,|S| \le 500,\sum |S| \le 1000\)

下发样例#3 满足\(n \le 3000,m \le 3000,|S| \le 3000\)

下发样例#4 满足第 \(i\) 个节点从第 \(i−1\) 个节点长出;

下发样例#5 满足所有询问中的 \(S\) 都相等;

下发样例#6 满足所有询问中的 \(x\) 都相等。

38 分做法

算是暴力了。

// The code was written by yifan, and yifan is neutral!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))

template<typename T>
inline T read() {
    T x = 0;
    bool fg = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        fg |= (ch == '-');
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return fg ? ~x + 1 : x;
}

template<typename T>
void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) {
        write(x / 10);
    }
    putchar(x % 10 + '0');
}

template<typename T>
void print(T x, char c) {
   write(x);
   putchar(c);
}

const int N = 4e5 + 5;

int n, m;
vector<int> t[N][26];
string s[N];

int idn(char c) {
    return (c - 'a');
}

void dfs(int u, int fa, string st) {
    s[u] = st;
    rep (i, 0, 25, 1) {
        if (t[u][i].size()) {
            for (int v : t[u][i]) {
                dfs(v, u, st + char('a' + i));
            }
        }
    }
}

int main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    n = read<int>();
    int op, j;
    char c;
    rep (i, 1, n, 1) {
        cin >> op;
        if (op == 1) {
            cin >> c;
            t[0][idn(c)].emplace_back(i);
        } else {
            cin >> j >> c;
            t[j][idn(c)].emplace_back(i);
        }
    }
    dfs(0, -1, "");
    m = read<int>();
    string str, tmp;
    int x;
    rep (i, 1, m, 1) {
        x = read<int>();
        cin >> str;
        tmp = s[x];
        int len = tmp.size(), l = str.size();
        int ans = 0;
        rep (j, 0, len - l + 1, 1) {
            int fg = 1;
            rep (k, 0, l - 1, 1) {
                if (tmp[j + k] != str[k]) {
                    fg = 0;
                    break ;
                }
            }
            ans += fg;
        }
        print(ans, '\n');
    }
    return 0;
}

正解:一堆优质题解

没错,这是 CF 上的原题!

随便找一份题解粘一下……

毕竟 AC 自动机又不在考纲里面

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define mar(o) for(int E=fst[o];E;E=e[E].nxt)
#define v e[E].to
using namespace std;
const int n7=401234,m7=801234,z7=1601234;
struct dino{int to,nxt;}e[z7];
struct lyca{int z,id;};
int n,T,cnt=1,tre[m7][26],fail[m7],poi[n7],head,tail,que[m7];
int ecnt,fst[m7],t,atre[n7],L[n7],R[n7],ans[n7];
char cr[n7];bool tru[m7][26];
vector <lyca> vec[m7];

int rd(){
   int shu=0;char ch=getchar();
   while(!isdigit(ch))ch=getchar();
   while(isdigit(ch))shu=(shu<<1)+(shu<<3)+(ch^48),ch=getchar();
   return shu;
}

void edge(int sta,int edn){
	ecnt++;
	e[ecnt]=(dino){edn,fst[sta]};
	fst[sta]=ecnt;
}

void insert1(){
	rep(i,1,n){
		int sys=rd(),las=1;
		if(sys==2)las=poi[ rd() ];
		char ch=getchar()-'a';
		if(!tre[las][ch]){
			cnt++,tre[las][ch]=cnt;
		}
		poi[i]=tre[las][ch];
	}
}

void insert2(int z){
	int len=strlen(cr+1),now=1;
	rep(i,1,len){
		int ch=cr[i]-'a';
		if(!tre[now][ch]){
			cnt++,tre[now][ch]=cnt;
		}
		now=tre[now][ch];
	}
	poi[z]=now;
}

void Gfail(){
	head=1,tail=1,que[1]=1;
	rep(i,0,25)tre[0][i]=1;
	while(head<=tail){
		int now=que[head];
		rep(i,0,25){
			int edn=tre[ fail[now] ][i];
			if(tre[now][i]){
				fail[ tre[now][i] ]=edn;
				edge(edn,tre[now][i]);
				tail++,que[tail]=tre[now][i];
				tru[now][i]=1;
			}
			else tre[now][i]=edn;
		}
		head++;
	}
}

#define lb(z) (z&-z)
void updat(int z,int id){
	while(id<=cnt)atre[id]+=z,id+=lb(id);
}
int Dquery(int id){
	int tot=0;
	while(id)tot+=atre[id],id-=lb(id);
	return tot;
}
int query(int l,int r){
	return Dquery(r)-Dquery(l-1);
}

void dfs1(int o){
	t++,L[o]=t;
	mar(o)dfs1(v);
	R[o]=t;
}

int fimd(){
	int len=strlen(cr+1),now=1;
	rep(i,1,len)now=tre[now][ cr[i]-'a' ];
	return now;
}

void dfs2(int o){
	updat(1,L[o]);
	int wal=vec[o].size()-1;
	rep(i,0,wal){
		int ll=L[ vec[o][i].z ];
		int rr=R[ vec[o][i].z ];
		ans[ vec[o][i].id ]=query(ll,rr);
	}
	rep(i,0,25){
		if(tru[o][i])dfs2(tre[o][i]); 
	}
	updat(-1,L[o]);
}

int main(){
	n=rd(),insert1(),T=rd();
	rep(i,1,T){
		int z=rd();scanf("%s",cr+1);
		insert2(i+n);
		vec[ poi[z] ].push_back( (lyca){poi[i+n],i} );
	}
	Gfail(),dfs1(1),dfs2(1);
	rep(i,1,T)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2023-10-17 22:16  yi_fan0305  阅读(14)  评论(0编辑  收藏  举报