做完一个题单,留念!
文章目录
- T1 [Censoring G](https://www.luogu.com.cn/problem/P3121)
- T2 [Word](https://www.luogu.com.cn/problem/P3966)
- T3 [Video Game G](https://www.luogu.com.cn/problem/P3041)
- T4 [VIDEO - Video game combos](https://www.luogu.com.cn/problem/SP10502)
- T5 [文本生成器](https://www.luogu.com.cn/problem/P4052)
- T6 [数数](https://www.luogu.com.cn/problem/P3311)
- T7 [阿狸的打字机](https://www.luogu.com.cn/problem/P2414)
- T8 [strings](https://www.luogu.com.cn/problem/T135693)
- T8 [e-Government](https://www.luogu.com.cn/problem/CF163E)
- T9 [Indie Album](https://www.luogu.com.cn/problem/CF1207G)
- T10 [[POI2000]病毒](https://www.luogu.com.cn/problem/P2444)
- T11 [You Are Given Some Strings...](https://www.luogu.com.cn/problem/CF1202E)
T1 Censoring G
跟Censoring S一样
考虑用栈
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e5+10;
char s[NNN],t[NNN];
int n,sz;
struct Trie{
int s[26];
int val,fail,last;
}ch[NNN];
int sta[NNN],top;
int rt[NNN];//记录栈内元素对应trie里的节点
inline void update(){
int p=0,len=strlen(t+1);
for(int i=1;i<=len;++i){
int c=t[i]-'a';
if(!ch[p].s[c]) ch[p].s[c]=++sz;
p=ch[p].s[c];
}
ch[p].val=len;
}
inline void get_fail(){
queue<int>q;
int p=0;
for(int i=0;i<=26;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
}
}
}
inline void query(){
int ans=0,len=strlen(s+1),p=0;
for(int i=1;i<=len;++i){
int c=s[i]-'a';
p=ch[p].s[c];
rt[i]=p;
sta[++top]=i;
if(ch[p].val){
top-=ch[p].val;
if(!top) p=0;
else p=rt[sta[top]];
}
}
}
int main(){
scanf("%s",s+1);
n=in;
for(int i=1;i<=n;++i){
scanf("%s",t+1);
update();
}
get_fail();
query();
for(int i=1;i<=top;++i)
putchar(s[sta[i]]);
return 0;
}
那个
if(!top) p=0; else p=rt[sta[top]];
加不加无所谓,加要快一点
T2 Word
多次搜索肯定炸
考虑把文章拼在一块,单词之间用奇怪的字符分开
注意判重
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e7+10;
int n,sz,lt;
char s[NNN],t[NNN];
struct Trie{
int s[26];
int fail,last,id;
}ch[NNN];
int ans[NNN],same[NNN];
inline void update(int id){
int p=0,len=strlen(s+1);
for(int i=1;i<=len;++i){
int c=s[i]-'a';
if(!ch[p].s[c]) ch[p].s[c]=++sz;
p=ch[p].s[c];
t[++lt]=s[i];
}
if(ch[p].id) same[id]=ch[p].id;
else ch[p].id=id;
t[++lt]='#';
return;
}
inline void get_fail(){
queue<int>q;
int p=0;
for(int i=0;i<26;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
ch[p].last=ch[ch[p].fail].id?ch[p].fail:ch[ch[p].fail].last;
}
}
}
inline void query(){
int p=0;
for(int i=1;i<=lt;++i){
int c=t[i]-'a';
p=ch[p].s[c];
int tmp=0;
if(ch[p].id) tmp=p;
else if(ch[p].last) tmp=ch[p].last;
while(tmp){
++ans[ch[tmp].id];
tmp=ch[tmp].last;
}
}
}
int main(){
n=in;
for(int i=1;i<=n;++i){
scanf("%s",s+1);
update(i);
}
get_fail();
query();
for(int i=1;i<=n;++i){
if(!same[i])printf("%d\n",ans[i]);
else printf("%d\n",ans[same[i]]);
}
return 0;
}
T3 Video Game G
ACM上DP
常见套路:设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示长度为i,末节点在ACM上节点j的最佳答案
d
p
[
i
]
[
s
o
n
[
p
]
]
=
max
{
d
p
[
i
−
1
]
[
p
]
+
v
a
l
[
s
o
n
[
p
]
]
}
dp[i][son[p]]=\max\{dp[i-1][p]+val[son[p]]\}
dp[i][son[p]]=max{dp[i−1][p]+val[son[p]]}
注意fail树上下放val值
注意枚举的范围,从根节点开始
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e3+10;
const int INF=1e9;
int n,k,sz,res;
char s[NNN];
struct Trie{
int s[3];
int val,fail,last;
}ch[NNN];
int dp[NNN][NNN];
inline void update(){
int p=0,len=strlen(s+1);
for(int i=1;i<=len;++i){
int c=s[i]-'A';
if(!ch[p].s[c]) ch[p].s[c]=++sz;
p=ch[p].s[c];
}
++ch[p].val;
}
inline void get_fail(){
queue<int>q;
int p=0;
for(int i=0;i<3;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
ch[p].fail=ch[p].last=0;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<3;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
}
ch[u].val+=ch[ch[u].fail].val;
}
}
inline void query(){
for(int i=0;i<=k;++i)
for(int j=0;j<=sz;++j)
dp[i][j]=-INF;
dp[0][0]=0;
for(int len=1;len<=k;++len)
for(int p=0;p<=sz;++p)
for(int j=0;j<3;++j)
dp[len][ch[p].s[j]]=max(dp[len][ch[p].s[j]],dp[len-1][p]+ch[ch[p].s[j]].val);
}
int main(){
n=in,k=in;
for(int i=1;i<=n;++i){
scanf("%s",s+1);
update();
}
get_fail();
query();
for(int i=1;i<=sz;++i)
res=max(res,dp[k][i]);
printf("%d\n",res);
return 0;
}
T4 VIDEO - Video game combos
跟T3没什么区别,双倍经验吧(复制粘贴打法好)
T5 文本生成器
考虑ACM上DP
设
d
p
[
i
]
[
j
]
[
0
/
1
]
dp[i][j][0/1]
dp[i][j][0/1]表示文章长
i
i
i,末节点为
j
j
j,不可读或可读的文章
易知
p
→
s
o
n
[
p
]
p\to son[p]
p→son[p]每一个字母都会覆盖到
考虑转移
- 对于单词末节点
d p [ l e n ] [ s o n [ p ] ] [ 1 ] = ∑ d p [ l e n − 1 ] [ p ] [ 0 ] + d p [ l e n − 1 ] [ p ] [ 1 ] dp[len][son[p]][1]=\sum dp[len-1][p][0]+dp[len-1][p][1] dp[len][son[p]][1]=∑dp[len−1][p][0]+dp[len−1][p][1] - 对于其他节点
d p [ l e n ] [ s o n [ p ] ] [ 0 ] = ∑ d p [ l e n − 1 ] [ p ] [ 0 ] d p [ l e n ] [ s o n [ p ] ] [ 1 ] = ∑ d p [ l e n − 1 ] [ p ] [ 1 ] dp[len][son[p]][0]=\sum dp[len-1][p][0]\\ dp[len][son[p]][1]=\sum dp[len-1][p][1] dp[len][son[p]][0]=∑dp[len−1][p][0]dp[len][son[p]][1]=∑dp[len−1][p][1]
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=2e4+10;
const int MOD=1e4+7;
int n,m,sz;
char s[NNN];
struct Trie{
int s[26];
int fail,last,val;
}ch[NNN];
int dp[200][NNN][2],ans;
inline void update(){
int p=0,len=strlen(s+1);
for(int i=1;i<=len;++i){
if(!ch[p].s[s[i]-'A']) ch[p].s[s[i]-'A']=++sz;
p=ch[p].s[s[i]-'A'];
}
ch[p].val=1;
}
inline void get_fail(){
queue<int>q;
int p=0;
for(int i=0;i<26;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
ch[p].last=ch[ch[p].fail].val?ch[p].fail:ch[ch[p].fail].last;
}
ch[u].val|=ch[ch[u].fail].val;
}
}
inline void query(){
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
for(int i=1;i<=m;++i)
for(int j=0;j<=sz;++j)
for(int k=0;k<26;++k){
if(ch[ch[j].s[k]].val)
dp[i][ch[j].s[k]][1]=(dp[i][ch[j].s[k]][1]+dp[i-1][j][0]+dp[i-1][j][1])%MOD;
else{
dp[i][ch[j].s[k]][0]=(dp[i][ch[j].s[k]][0]+dp[i-1][j][0])%MOD;
dp[i][ch[j].s[k]][1]=(dp[i][ch[j].s[k]][1]+dp[i-1][j][1])%MOD;
}
}
}
signed main(){
n=in,m=in;
for(int i=1;i<=n;++i){
scanf("%s",s+1);
update();
}
get_fail();
query();
for(int i=0;i<=sz;++i)
ans=(ans+dp[m][i][1])%MOD;
printf("%lld\n",ans);
return 0;
}
看题看清楚,写数写仔细
模数写错了,D我半小时 😭 😭
T6 数数
fail 树上下放 val ,数位dp
数位dp注意维护前导 0 和上限
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=3e3+5;
const int MOD=1e9+7;
int m,top,sz;
char n[NNN],s[NNN];
struct Trie{
int s[10];
int val,fail;
}ch[NNN];
int f[NNN][NNN];
inline void update(){
int p=0,len=strlen(s+1);
for(int i=1;i<=len;++i){
if(!ch[p].s[s[i]-'0']) ch[p].s[s[i]-'0']=++sz;
p=ch[p].s[s[i]-'0'];
}
ch[p].val=1;
return;
}
inline void get_fail(){
int p=0;queue<int>q;
for(int i=0;i<10;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<10;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
}
ch[u].val+=ch[ch[u].fail].val;
}
}
inline int dp(int pos/*数位*/,int p/*节点*/,int lim/*上限*/,int lead/*前导零*/){
if(pos>top) return !lead;//考虑前导零
if(lim&&f[pos][p]!=-1) return f[pos][p];
int res=0;
for(int i=0;i<10;++i){
if(!lim&&i>n[pos]-'0') break;
if(lead){
if(!ch[ch[0].s[i]].val)
res=(res+dp(pos+1,ch[0].s[i],lim||i<n[pos]-'0',lead&&!i))%MOD;
}
else{
if(!ch[ch[p].s[i]].val)
res=(res+dp(pos+1,ch[p].s[i],lim||i<n[pos]-'0',0))%MOD;
}
}
if(lim&&!lead) f[pos][p]=res;
return res;
}
signed main(){
scanf("%s",n+1);
top=strlen(n+1);
m=in;
for(int i=1;i<=m;++i){
scanf("%s",s+1);
update();
}
get_fail();
memset(f,-1,sizeof(f));
printf("%lld\n",dp(1,0,0,1));
return 0;
}
T7 阿狸的打字机
定义第
x
x
x个打印的字符串为模式串,第
y
y
y个打印的字符串为文本串(字符串问题)
可以观察到,模式串末节点被 fail 指了就会有一个贡献
而只有文本串上的、指向模式串末节点的 fail 才有贡献
考虑点亮文本串上的所有节点,跑 fail
众所周知, fail 是棵树
考虑树剖,这玩意儿优 个鬼
DFS序 + 树状数组比它优
- 首先,打字机是个栈,得到所有字符串,建 trie(显然树状数组着建要优一些,栈不优)
- 然后,边get_fail 边建 fail树,DFS序 编号
- 接着,对于所有询问,因为离线,可以排序,对于文本串相同的多组数据,可以优地统计模式串
(已知区间/区间固定,这也就决定了根据单点改,区间和) - 最后,单点修改,遇到根节点,区间求和(差分)统计子树,注意不往下找的判据是儿子深度比爸爸低(get_fail 的时候没这个儿子,就跳到 fail 儿子去了),over
#include<bits/stdc++.h>
#define in Read()
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e6+10;
char txt[NNN];
struct Trie{
int s[26];
int val,fail;
int dep;//fail树上深度
}ch[NNN];
int f[NNN];//trie上节点父亲
int en[NNN],n;//每个字符串的末节点
int sz;
inline void build(){
scanf("%s",txt+1);
int p=0,len=strlen(txt+1),c;
for(int i=1;i<=len;++i){
if(txt[i]=='B'){
p=f[p];
continue;
}
if(txt[i]=='P'){
ch[p].val=++n;
en[n]=p;
continue;
}
c=txt[i]-'a';
if(!ch[p].s[c]) ch[p].s[c]=++sz;
f[ch[p].s[c]]=p;
p=ch[p].s[c];
}
return;
}
struct Road{
int nxt,to;
}road[NNN<<1];
int tot_road,first[NNN];
inline void add(int u,int v){
++tot_road;
road[tot_road].nxt=first[u];
first[u]=tot_road;
road[tot_road].to=v;
}
inline void get_fail(){
queue<int>q;
int p=0;
for(int i=0;i<26;++i){
p=ch[0].s[i];
if(!p) continue;
q.push(p);
ch[p].fail=0;
ch[p].dep=ch[0].dep+1;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i){
p=ch[u].s[i];
if(!p){
ch[u].s[i]=ch[ch[u].fail].s[i];
continue;
}
q.push(p);
int v=ch[u].fail;
while(v&&!ch[v].s[i]) v=ch[v].fail;
ch[p].fail=ch[v].s[i];
ch[p].dep=ch[u].dep+1;
}
add(u,ch[u].fail),add(ch[u].fail,u);
}
}
int dfn[NNN];//DFS序/时间戳
int siz[NNN];//子树大小
int tot;//DFS序
inline void Dfs(int fa,int u){
int v;
dfn[u]=++tot;
siz[u]=1;
for(int e=first[u];e;e=road[e].nxt){
v=road[e].to;
if(!(v^fa)) continue;
Dfs(u,v);
siz[u]+=siz[v];
}
}
int m;
struct Query{
int x,y,id,ans;
}qur[NNN];
int where[NNN];//记录排序后每种文本串的第一个询问
inline bool cmp1(const Query &u,const Query &v){return u.y<v.y;}
inline bool cmp2(const Query &u,const Query &v){return u.id<v.id;}
inline void get_query(){
m=in;
for(int i=1;i<=m;++i){
qur[i].x=in,qur[i].y=in;
qur[i].id=i;
}
sort(qur+1,qur+m+1,cmp1);
for(int i=1;i<=m;++i)
if(qur[i].y^qur[i-1].y)
where[qur[i].y]=i;
}
int tree[NNN];
inline int lowbit(int x){return x&(-x);}
inline void update(int p,int w){
while(p<=tot){
tree[p]+=w;
p+=lowbit(p);
}
}
inline int query(int p){
int res=0;
while(p){
res+=tree[p];
p-=lowbit(p);
}
return res;
}
inline void work(int id){
if(!where[id]) return;
int i=where[id],p;
while(true){
p=en[qur[i].x];
qur[i].ans=query(dfn[p]+siz[p]-1)-query(dfn[p]-1);
if(qur[i+1].y^qur[i].y) break;
++i;
}
}
inline void search(int u){
update(dfn[u],1);
if(ch[u].val) work(ch[u].val);
int p;
for(int i=0;i<26;++i){
p=ch[u].s[i];
if(!p) continue;
if(ch[p].dep<=ch[u].dep) continue;
search(p);
}
update(dfn[u],-1);
}
inline void print(){
sort(qur+1,qur+m+1,cmp2);
for(int i=1;i<=m;++i)
printf("%d\n",qur[i].ans);
}
int main(){
build();
get_fail();
Dfs(0,0);
get_query();
search(0);
print();
return 0;
}
T8 strings
考试题,当时不会,现在会了
ACM 上 DP,稍微改了一下 val 的定义
设
d
p
i
,
j
dp_{i,j}
dpi,j表示长度为
i
i
i,字典树上
j
j
j节点的最优方案
考虑转移方程:
d
p
i
,
s
o
n
p
=
max
{
d
p
i
−
1
,
p
+
v
a
l
s
o
n
p
}
dp_{i,son_p}=\max\{dp_{i-1,p}+val_{son_p}\}
dpi,sonp=max{dpi−1,p+valsonp}
#include<bits/stdc++.h>
#define in Read()
#define int long long
using namespace std;
inline int in{
int i=0,f=1;char ch;
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(isdigit(ch))i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int NNN=1e3+10;
const int INF=1e9+10;
int n,L,a[NNN],sz;
char s[NNN];
int ch[NNN][26],fail[NNN],val[NNN];
int dp[NNN][NNN],ans;
int same[NNN],idx[NNN];
inline void build(int id){
int p=0,len=strlen(s+1);
for(int i=1;i<=len;++i){
if(!ch[p][s[i]-'a']) ch[p][s[i]-'a']=++sz;
p=ch[p][s[i]-'a'];
}
if(idx[p]) same[id]=idx[p],val[p]+=a[id];
else val[p]=a[id],idx[p]=id;
}
inline void get_fail(){
queue<int>q;int p=0;
for(int i=0;i<26;++i){
p=ch[0][i];
if(!p) continue;
q.push(p);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;++i){
p=ch[u][i];
if(!p){
ch[u][i]=ch[fail[u]][i];
continue;
}
q.push(p);
int v=fail[u];
while(v&&!ch[v][i]) v=fail[v];
fail[p]=ch[v][i];
}
val[u]+=val[fail[u]];
}
}
inline void query(){
for(int i=0;i<=L;++i)
for(int j=0;j<=sz;++j)
dp[i][j]=-INF;
dp[0][0]=0;
for(int i=1;i<=L;++i)
for(int j=0;j<=sz;++j)
for(int c=0;c<26;++c)
dp[i][ch[j][c]]=max(dp[i][ch[j][c]],dp[i-1][j]+val[ch[j][c]]);
}
signed main(){
n=in,L=in;
for(int i=1;i<=n;++i) a[i]=in;
for(int i=1;i<=n;++i){
scanf("%s",s+1);
build(i);
}
get_fail();
query();
for(int i=0;i<=sz;++i){
if(!same[i]) ans=max(ans,dp[L][i]);
else ans=max(ans,dp[L][same[i]]);
}
printf("%lld\n",ans);
return 0;
}
T8 e-Government
类似 T7,且显然暴力 T 飞
但是不像 T7,在线,没有排序的优化了,
O
(
n
log
n
)
O(n\log n)
O(nlogn)
无法排序也就决定区间不定,因此只能单点改,区间和
首先,建 trie,边 get_fail 边建树
然后,DFS排序,区间加上传现有单词集
最后,对于每个操作,区间加/单点改
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while (!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while (isdigit(ch))
i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e6+5;
int n,m,sz;
char s[N];
bool vis[N];
int tr[N][26],pos[N],idx[N];
int fail[N];
int tot,first[N],nxt[N<<1],aim[N<<1];
int dfn[N],siz[N],ord;
int tre[N];
void ljb(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
void build(int id){
int len=strlen(s),p=0;
for(int i=0;i<len;++i){
int c=s[i]-'a';
if(!tr[p][c]) tr[p][c]=++sz;
p=tr[p][c];
}
pos[id]=p;
idx[p]=id;
return;
}
void get_fail(){
queue<int> q;
int p=0;
for(int i=0;i<26;++i){
p=tr[0][i];
if(!p) continue;
q.push(p);
fail[p]=0;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int c=0;c<26;++c){
p=tr[u][c];
if(!p){
tr[u][c]=tr[fail[u]][c];
continue;
}
q.push(p);
int v=fail[u];
while(v&&!tr[v][c]) v=fail[v];
fail[p]=tr[v][c];
}
ljb(u,fail[u]);
ljb(fail[u],u);
}
return;
}
void DFS(int u,int fa){
// printf("%d %d\n",u,fa);
dfn[u]=++ord;
siz[u]=1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS(v,u);
siz[u]+=siz[v];
}
return;
}
int lowbit(int x){
return x&(-x);
}
void add(int p,int v){
// cout<<p<<endl;
while(p<=ord){
tre[p]+=v;
p+=lowbit(p);
}
return;
}
int query(int p){
int res=0;
while(p){
res+=tre[p];
p-=lowbit(p);
}
return res;
}
int find(){
int p=0;
int ans=0;
int len=strlen(s);
for(int i=0;i<len;++i){
int c=s[i]-'a';
p=tr[p][c];
ans+=query(dfn[p]);
}
return ans;
}
int main(){
n=in,m=in;
for(int i=1;i<=m;++i){
scanf("%s",s);
build(i);
}
get_fail();
DFS(0,0);
for(int i=1;i<=m;++i){
// cout<<pos[i]<<"=="<<endl;
vis[i]=true;
int p=pos[i];
add(dfn[p],1);
add(dfn[p]+siz[p],-1);
}
for(int i=1;i<=n;++i){
char op=getchar();
while(op!='+'&&op!='-'&&op!='?')
op=getchar();
if(op=='+'){
int x=in;
if(vis[x]) continue;
vis[x]=true;
x=pos[x];
add(dfn[x],1);
add(dfn[x]+siz[x],-1);
}else if(op=='-'){
int x=in;
if(!vis[x]) continue;
vis[x]=false;
x=pos[x];
add(dfn[x],-1);
add(dfn[x]+siz[x],1);
}else if(op=='?'){
scanf("%s",s);
printf("%d\n",find());
}
}
return 0;
}
T9 Indie Album
都说这是多倍经验怎么我觉得它们都挺不同的呢
离线下来做即可
trie树上深搜,如果遇到要求的点就统计它的贡献
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while (!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while (isdigit(ch))
i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e6+5;
int n,m,sz;
int tr[N][26];
int val[N],sam[N],pos[N],sm[N];
int fail[N];
char s[N];
int ww[N];
int dep[N];
int tot,first[N],nxt[N<<1],aim[N<<1];
int ord,dfn[N],siz[N];
int tre[N];
struct Query{
int x,y,id,ans;
}qur[N];
bool cmp1(const Query &u,const Query &v){
return u.y<v.y;
}
bool cmp2(const Query &u,const Query &v){
return u.id<v.id;
}
void ljb(int u,int v){
++tot;
nxt[tot]=first[u];
first[u]=tot;
aim[tot]=v;
return;
}
int lowbit(int x){
return x&(-x);
}
void add(int p,int v){
while(p<=ord){
tre[p]+=v;
p+=lowbit(p);
}
return;
}
int query(int p){
int res=0;
while(p){
res+=tre[p];
p-=lowbit(p);
}
return res;
}
void insert(int p,int c,int id){
if(!tr[p][c])
tr[p][c]=++sz;
p=tr[p][c];
if(val[p]) sm[id]=val[p];
else val[p]=id;
pos[id]=p;
return;
}
int build(int id){
int p=0;
int len=strlen(s);
for(int i=0;i<len;++i){
int c=s[i]-'a';
if(!tr[p][c])
tr[p][c]=++sz;
p=tr[p][c];
}
return p;
}
void scan(){
n=in;
for(int i=1;i<=n;++i){
int op=in;
if(op==1){
char c=getchar();
while(!isalpha(c))
c=getchar();
insert(0,c-'a',i);
}else{
int p=in;
char c=getchar();
while(!isalpha(c))
c=getchar();
insert(pos[p],c-'a',i);
}
}
m=in;
for(int i=1;i<=m;++i){
qur[i].y=in,qur[i].id=i;
if(sm[qur[i].y])
qur[i].y=sm[qur[i].y];
scanf("%s",s);
qur[i].x=build(i);
}
sort(qur+1,qur+m+1,cmp1);
for(int i=1;i<=m;++i){
if(qur[i].y==qur[i-1].y) continue;
ww[qur[i].y]=i;
}
return;
}
void get_fail(){
queue<int> q;
int p=0;
for(int i=0;i<26;++i){
p=tr[0][i];
if(!p) continue;
q.push(p);
fail[p]=0;
dep[p]=dep[0]+1;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int c=0;c<26;++c){
p=tr[u][c];
if(!p){
tr[u][c]=tr[fail[u]][c];
continue;
}
q.push(p);
int v=fail[u];
while(v&&!tr[v][c]) v=fail[v];
fail[p]=tr[v][c];
dep[p]=dep[u]+1;
}
ljb(u,fail[u]);
ljb(fail[u],u);
}
return;
}
void DFS(int u,int fa){
dfn[u]=++ord;
siz[u]=1;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(v==fa) continue;
DFS(v,u);
siz[u]+=siz[v];
}
return;
}
void work(int id){
int i=ww[id];
while(qur[i].y==id){
int u=qur[i].x;
qur[i].ans=
query(dfn[u]+siz[u]-1)-
query(dfn[u]-1);
++i;
}
return;
}
void search(int u){
add(dfn[u],1);
if(val[u]) work(val[u]);
for(int c=0;c<26;++c){
int p=tr[u][c];
if(dep[p]<=dep[u]) continue;
search(p);
}
add(dfn[u],-1);
return;
}
void print(){
sort(qur+1,qur+m+1,cmp2);
for(int i=1;i<=m;++i)
printf("%d\n",qur[i].ans);
return;
}
int main(){
scan();
get_fail();
DFS(0,0);
search(0);
print();
return 0;
}
T10 [POI2000]病毒
手玩发现在 ACM 上存在没有标记的环即有无限长的安全代码,找环即可
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while (!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while (isdigit(ch))
i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=1e5+5;
int n,sz;
char s[N];
int tr[N][2],fail[N];
bool val[N],vis[N],tag[N];
void build(){
int p=0;
int len=strlen(s);
for(int i=0;i<len;++i){
int c=s[i]-'0';
if(!tr[p][c])
tr[p][c]=++sz;
p=tr[p][c];
}
val[p]=true;
return;
}
void get_fail(){
queue<int> q;
int p=0;
for(int c=0;c<2;++c){
p=tr[0][c];
if(!p) continue;
q.push(p);
fail[p]=0;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int c=0;c<2;++c){
int p=tr[u][c];
if(!p){
tr[u][c]=tr[fail[u]][c];
continue;
}
q.push(p);
int v=fail[u];
while(v&&!tr[v][c]) v=fail[v];
fail[p]=tr[v][c];
}
val[u]+=val[fail[u]];
}
return;
}
void work(int p){
if(tag[p]){
puts("TAK");
exit(0);
}
if(val[p]||vis[p]) return;
tag[p]=vis[p]=true;
for(int c=0;c<2;++c){
if(tr[p][c])
work(tr[p][c]);
}
tag[p]=false;
return;
}
int main(){
n=in;
for(int i=1;i<=n;++i){
scanf("%s",s);
build();
}
if(!tr[0][0]||!tr[0][1]){
puts("TAK");
return 0;
}
get_fail();
work(0);
puts("NIE");
return 0;
}
T11 You Are Given Some Strings…
正做一边,反做一遍
枚举拼接点,乘起来即可
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
int in{
int i=0,f=1;char ch=0;
while (!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while (isdigit(ch))
i=(i<<1)+(i<<3)+ch-48, ch=getchar();
return i*f;
}
const int N=3e5+5;
int n,res;
struct ACM{
int sz;
int val[N];
int ans[N];
int fail[N];
int last[N];
int tr[N][26];
char txt[N],s[N];
void build(){
int p=0;
int len=strlen(s);
for(int i=0;i<len;++i){
int c=s[i]-'a';
if(!tr[p][c])
tr[p][c]=++sz;
p=tr[p][c];
}
++val[p];
}
void get_fail(){
queue<int> q;
int p=0;
for(int c=0;c<26;++c){
p=tr[0][c];
if(!p) continue;
q.push(p);
fail[p]=0;
}
while(!q.empty()){
int u=q.front();q.pop();
for(int c=0;c<26;++c){
p=tr[u][c];
if(!p){
tr[u][c]=tr[fail[u]][c];
continue;
}
q.push(p);
int v=fail[u];
while(v&&!tr[v][c]) v=fail[v];
fail[p]=tr[v][c];
last[p]=val[fail[p]]?fail[p]:last[fail[p]];
}
}
return;
}
void find(){
int p=0;
int len=strlen(txt);
for(int i=0;i<len;++i){
int c=txt[i]-'a';
p=tr[p][c];
int res=0;
int v=val[p]?p:last[p];
while(v){
res+=val[v];
v=last[v];
}
ans[i]=res;
}
return;
}
}acm1,acm2;
void get_ans(){
res=0;
int len=strlen(acm1.txt);
for(int i=0;i<len;++i)
res+=acm1.ans[i]*acm2.ans[len-i-2];
return;
}
signed main(){
scanf("%s",acm1.txt);
memcpy(acm2.txt,acm1.txt,sizeof acm2.txt);
reverse(acm2.txt,acm2.txt+strlen(acm2.txt));
n=in;
for(int i=1;i<=n;++i){
scanf("%s",acm1.s);
acm1.build();
memcpy(acm2.s,acm1.s,sizeof acm2.s);
reverse(acm2.s,acm2.s+strlen(acm2.s));
acm2.build();
}
acm1.get_fail();
acm2.get_fail();
acm1.find();
acm2.find();
get_ans();
printf("%lld\n",res);
return 0;
}