7.21csp-s模拟4(难度noip+版)

csp-s 模拟4(难度noip+版)

太难了qwq,保龄了。题解发下来发现一个题解也看不懂。

T1 前缀 (lcp)

给出\(n\)\(m\)以及一个长是\(n\)的字符串\(S\),要求构造一个长度为\(m\)的字符串\(T\),使\(\sum_{i=1}^{m}{Lcp(S,T[i...m])}\)最大,并求出最大值。

\(Lcp(S,T[i...m])\)\(T\)从第\(i\)个位置开始的后缀和\(S\)的最长公共前缀的长度

首先考虑暴力:我们可以直接枚举每一位\(1\) \(-\) \(maxn\) , 暴力算出最大值。复杂度\(O(m^m\times maxn)\) ,能够得到高达5分

其次考虑正解:

最大值当然考虑用dp,而字符串可以考虑AC自动机或者KMP算法,但是我更喜欢AC自动机。

样例:

5 8

1 2 1 1 2

注意到:

题目可以转化为:

用字符串T去匹配S的前缀:

1

1 2

1 2 1

1 2 1 1

1 2 1 1 2

我们建立AC自动机在AC自动机上跑DP

定义\(f{[i]}{[j]}\)为刚好匹配到长度为i,当前状态是j的lcp最大值

可以推出转移方程 \(f{[tr{[j][k]}]}{[i]} = max({f{[i]}{[j]} + tr[to].ans})\)

#include<bits/stdc++.h>
using namespace std;

const int N = 5200 ; 
int n , m  , S[N] , f[N][N] ; 

struct trie{
    int son[N] , fail , ans ; 
}tr[N];

int maxn ;
int cnt ; 

inline void build(int len){
    int u = 0 ; 
    for(int i = 1 ; i <= len ; i++){
        int &son = tr[u].son[S[i]];
        if(!son){
            son = ++cnt ; 
        }
        u = son ; 
    }
    tr[u].ans++ ; 
}

inline void AC(){
    queue<int>q;
    int p = 0 ; 
    for(int i = 1 ; i <= maxn ;i++){
        if(tr[p].son[i])q.push(tr[p].son[i]);
    }
    while(!q.empty()){
        int u = q.front();
        tr[u].ans += tr[tr[u].fail].ans ; 
        q.pop();
        for(int i = 1 ; i <= maxn ;i++){
            if(tr[u].son[i]){
                tr[tr[u].son[i]].fail = tr[tr[u].fail].son[i];
                q.push(tr[u].son[i]);
            }
            else{
                tr[u].son[i] = tr[tr[u].fail].son[i];
            }
        }        
    }
}

int len[N] ; 

int main(){
    freopen("lcp.in","r",stdin);
    freopen("lcp.out","w",stdout);
    cin >> n >> m ; 
    for(register int  i = 1 ; i <= n ; i++){
        cin >> S[i];
        maxn = max( maxn , S[i]);
        build(i);
    }
    AC();
    for(register int  i = 0 ; i <= n ; i++ ){
        sort(tr[i].son + 1 , tr[i].son + 1 + maxn);
        len[i] = unique( tr[i].son + 1 , tr[i].son + 1 + maxn) - ( tr[i].son + 1 );
    }
    memset( f , -0x3f , sizeof(f));
    f[0][0] = 0;
    for(register int  i = 1 ; i <= m ; i++ ){
        for(register int  j = 0 ; j <= n ; j++){
            for(register int  k = 1 ; k <= len[j] ; k++){
                int to = tr[j].son[k];
                f[to][i] = max( f[to][i] ,  f[j][i - 1] + tr[to].ans);
            }            
        }
    }
    int ans = 0; 
    for(register int i = 0   ; i <= n ;i++ ){
        ans = max( ans ,  f[i][m] );
    }
    cout << ans << endl ; 

    return 0;
}

t2 两条 (graph)

看见我满篇调试了吗 知道我有多崩溃吗??

给出两个操作

1.删除某一条边

2.询问两点间有没有不相交的两条路径

因为昨天刚做了圆方树习题,所以看见就应激写了个圆方树,结果发现是割点,假了qwq

考虑删掉一条边非常不好维护,所以采用离线操作

从后往前做

这样删边就转化成加边

#include<bits/stdc++.h>
using namespace std;
const int N = 3e6 + 20 ; 
int n , m , qb , tid ; 
vector<int>G[N];
vector<string>ans; 
struct edge{
    int y , x , ti ; 
}E[N];
bool cmp(edge a1 , edge b1){
    return a1.ti > b1.ti ; 
}
struct que{
    int op , x , y;
}q[N];
int del[N];
struct tp{
    int fa[N];
    int find( int x ){
        if(fa[x] != x){
            return fa[x] = find(fa[x]);
        }
        return x ; 
    }
    void merge(int x , int y){
        int xx = find(x) , yy = find(y);
        // cout << xx << " " << yy << endl ;
        if( xx != yy )fa[xx] = yy ; 
    }
}tr , sc , te ;// 
int dep[N];
int fa[N];
void DEP(int u , int dp){
    dep[u] = dp ; 
    for(auto v : G[u]){
        if(!dep[v])DEP( v , dp + 1 );    
    }
}
int Fa(int u ){return sc.find(fa[u]);}

int lca( int a , int b ){
    if( dep[a] < dep[b] )swap(a , b);
    while( a != b ){
    // cerr << a << " " << b << endl ; 
        if(dep[a] < dep[b])swap(a , b );
        a = Fa(a) ;
    }
    return a ;
}


// int Lca(int u, int v){
// 	while(u != v){
// 		if(dep[u] < dep[v]) swap(u, v);
// 		u = Fa(u);
// 	}
// 	return u;
// }
void ADD(int i){
    int u = E[i].x , v = E[i].y ;
    if(dep[u] < dep[v]) swap(u, v);
	u = sc.find(u), v = sc.find(v);
    // cout << i << " " << u << " " << v << endl ; 
	if(tr.find(u) == tr.find(v)){
		int d = lca(u, v), f;
        // cerr << i << " " << "awawa " << endl ;  
		while(u != d) f = Fa(u), sc.merge(u, d), u = f;
		while(v != d) f = Fa(v), sc.merge(v, d), v = f;
	}else{
		fa[u] = v, tr.merge(u, v);
	}
}
// void Add(int i){
// 	auto [u, v] = ce[i]; if(dep[u] < dep[v]) swap(u, v);
// 	u = sc.Fa(u), v = sc.Fa(v);
// 	if(tr.Fa(u) == tr.Fa(v)){
// 		int d = Lca(u, v), f;
// 		while(u != d) f = Fa(u), sc.Uni(u, d), u = f;
// 		while(v != d) f = Fa(v), sc.Uni(v, d), v = f;
// 	}else{
// 		assert(!fa[u]);
// 		fa[u] = v, tr.Uni(u, v);
// 	}
// }
struct eg{
    int u , v , w ; 
};
vector<tuple<int, int, int>> egg;
int main(){ 
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    // cout << "awaaaawa" << endl; 
    cin >> n >> m >> qb >> tid ;
    for(int i = 1 ; i <= m ; i++ ){
        cin >> E[i].x >> E[i].y ;
        // E[i].ti = m - i ;  
    }
    for(int i = 1 ; i <= n * 2; i++){
        tr.fa[i] = te.fa[i] = sc.fa[i] = i ;  
    }
    for(int i = 1 ; i <= qb ; i++ ){
        cin >> q[i].op >> q[i].x;
        if(q[i].op == 2)cin >> q[i].y;
        if(q[i].op == 1)del[q[i].x] = - qb + i - 1;  ; 
    }
    // sort( E + 1 , E + 1 + m , cmp );
    // for(int i = 1 ; i <= m ; i++ ){
    //     cout << E[i].ti << endl ; 
    // }
	for(int i = 1; i <= m; ++i){
		int u = E[i].x , v = E[i].y ; int w = del[i] ? del[i] : m - i;
		egg.push_back({w, u, v});
	}
	sort(egg.begin(), egg.end(), greater<>());
    for(auto [w, u, v] : egg) if(te.find(u) != te.find(v)) G[u].push_back(v), G[v].emplace_back(u), te.merge(u, v); 
	
    // for(int i = 1 ; i <= m ; i++ ){
    //     int u = egg[i].a , v = egg[i].y ;
    //     if( te.find(u) != te.find(v)){
    //         // cout << v << " " << u << endl ; 
    //         // cout << te.find(v) << " " << te.find(u) << endl ; 
    //         te.merge( u , v );
    //         G[u].push_back(v);
    //         G[v].push_back(u);
    //     }       
    // }
    
    for(int i = 1 ; i <= n ; i++ ){if(!dep[i]){DEP( i , 1 );}}
    for(int i = 1 ; i <= m ; i++ ){if(!del[i]){ADD(i);}}   
    for(int i = qb ; i >= 1 ; i-- ){
        // cout << sc.find(q[i].x) << "  " <<  sc.find(q[i].y) << endl ; 
        if(q[i].op == 1)ADD(q[i].x);
        else{
            if( sc.find(q[i].x) == sc.find(q[i].y)){
                ans.push_back("YES") ; 
            }
            else{
                // cerr << sc.find(q[i].x) << ' ' << sc.find(q[i].y) << '\n';
                ans.push_back("NO");
            }
        }
    }
    reverse(ans.begin(),ans.end());
    for(auto v : ans)cout << v << endl ;
    return 0;   
}




t3 摧毁 (destory)

我不会网络流扫描线(等我更新)。

t4 速速 (speed)

我更不会(等我更新)。

posted @ 2025-07-21 22:11  Nailong2357  阅读(43)  评论(0)    收藏  举报