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)
我更不会(等我更新)。

浙公网安备 33010602011771号