完全做不动啊啊啊啊啊啊啊
给定 n n n 个仅包含 a , b a,b a,b 的字符串。
你需要去掉尽可能少的字符串,使得剩下的字符串中不存在某一个串是另一个串的子串。
A C AC AC自动机上路径压缩求出 D A G DAG DAG,
再传递闭包后求最长反链。
拆点构图后求出最小链覆盖和方案。
通过最小链覆盖构造出最长反链。
A C C o d e \mathcal AC \ Code AC Code
#include
#define maxn 755
#define maxc 2
#define maxp 20000007
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
int n;
char s[maxp],*S[maxn];
int tr[maxp][maxc] , len[maxn] , fa[maxp] , tag[maxp] , tot;
int q[maxp],L,R;
int c[maxn][maxn],cx[maxn],cy[maxn],ca[maxn];
int vis[maxn],tim;
int dfs(int u){
rep(i,1,n) if(i!=u && c[u][i] && vis[i]!=tim){
vis[i] = tim;
if(!cy[i] || dfs(cy[i])){
cy[i] = u , cx[u] = i;
return 1;
}
}
return 0;
}
vector<vector<int> >Path;
void ser(int u,vector<int>&P){
P.push_back(u);
if(!cy[u]) return;
ser(cy[u],P);
}
int main(){
scanf("%d",&n);
tot = 1;
rep(i,1,n){
scanf("%s",s);
int L = strlen(s) , u = 1;
S[i] = new char[L + 2];
len[i] = L;
memcpy(S[i],s,sizeof(char) * L);
fa[u] = 0;
rep(i,0,L-1){
int v = s[i] - 'a';
if(!tr[u][v]) tr[u][v] = ++tot;
u = tr[u][v];
}
tag[u] = i;
}
L=0,R=-1;
rep(i,0,1) if(tr[1][i]) q[++R] = tr[1][i] , fa[tr[1][i]] = 1;
else tr[1][i] = 1;
for(int u;L<=R;){
u = q[L++];
rep(i,0,1) if(tr[u][i]) fa[tr[u][i]] = tr[fa[u]][i] , q[++R] = tr[u][i];
else tr[u][i] = tr[fa[u]][i];
}
rep(i,1,n){
int u=1,v;
rep(j,0,len[i]-1){
u = tr[u][S[i][j] - 'a'];
for(v=u;v!=1 && (tag[v] == 0 || tag[v] == i);v=fa[v]);
c[i][tag[v]] = 1;
for(int p=u,t;p!=v;p=t)
t = fa[p] , fa[p] = v;
}
}
rep(k,1,n) rep(i,1,n) rep(j,1,n)
c[i][j] |= c[i][k] & c[k][j];
rep(i,1,n) c[i][i] = 0;
int ans = 0;
rep(i,1,n) if(!cx[i]) ++tim,ans+=dfs(i);
printf("%d\n",n-ans);
rep(i,1,n) if(!cx[i]){
vector<int>r;
ser(i,r);
reverse(r.begin(),r.end());
Path.push_back(r);
}
bool flg = 1;
memset(ca,0,sizeof ca);
while(flg){
flg = 0;
rep(i,0,Path.size()-1)
rep(j,1,n) if(c[Path[i].back()][j] == 1)
ca[j] = 1;
rep(i,0,Path.size()-1) while(ca[Path[i].back()]){
flg = 1;
Path[i].pop_back();
}
}
rep(i,0,Path.size()-1) printf("%d%c",Path[i].back()," \n"[i==Path.size()-1]);
}
给出一个串 S S S
求一个最长字符串序列 { T i } \{T_i\} { Ti}满足所有元素都是 S S S的子串,并且 T i T_i Ti在 T i − 1 T_{i-1} Ti−1中出现了至少两次。
建出后缀自动机。
可以通过反证得到 endpos \texttt{endpos} endpos相同的节点中选长的一定不劣。(劣的话 endpos \texttt{endpos} endpos就不会相同。)
所以就从根往下贪心,能选则选(用线段树合并得到 endpos \texttt{endpos} endpos用于判断),适时回退即可。
#include
#define maxn 400005
#define lim 20
#define maxc 26
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char Start;
int n,m;
char S[maxn],T[maxn];
int fa[maxn],tr[maxn][maxc],len[maxn],rt[maxn],pos[maxn],tot,last;
vector<int>G[maxn];
void ins(int c,bool debug = 0){
if(tr[last][c]){
int p = last , q = tr[last][c];
if(len[q] != len[p] + 1){
int v=++tot;
memcpy(tr[v],tr[q],sizeof tr[q]);
fa[v]=fa[q],len[v]=len[p]+1;
for(;~p && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = v;
}
}
else{
int u=++tot,p=last,q;
len[u]=len[p]+1;
for(;~p && !tr[p][c];p=fa[p]) tr[p][c]=u;
if(p==-1) fa[u]=0;
else if(len[q=tr[p][c]]==len[p]+1) fa[u]=q;
else{
int v=++tot;
memcpy(tr[v],tr[q],sizeof tr[q]);
fa[v]=fa[q],len[v]=len[p]+1;
for(;~p && tr[p][c] == q;p=fa[p]) tr[p][c] = v;
fa[q] = fa[u] = v;
}
}
}
#define maxp maxn * 30
int lc[maxp],rc[maxp],mx[maxp],mxp[maxp];
void upd(int u){
if(mx[lc[u]] >= mx[rc[u]]) mx[u] = mx[lc[u]] , mxp[u] = mxp[lc[u]];
else mx[u] = mx[rc[u]] , mxp[u] = mxp[rc[u]];
}
void ins(int &u,int l,int r,int p){
if(!u) u = ++tot , mxp[u] = l;
if(l==r) return (void)(mx[u]++);
int m = l+r>>1;
p <= m ? ins(lc[u],l,m,p) : ins(rc[u],m+1,r,p);
upd(u);
}
void merge(int &u,int l,int r){
if(!l || !r) return (void)(u=l+r);
u = ++tot;
if(lc[l] || rc[l]){
merge(lc[u],lc[l],lc[r]),merge(rc[u],rc[l],rc[r]);
upd(u);
}
else{
mx[u] = mx[l] + mx[r] , mxp[u] = mxp[l];
}
}
pair<int,int> qry(int u,int l,int r,int ql,int qr){
if(ql>r||l>qr||!u||ql>qr) return make_pair(0,0x3f3f3f3f);
if(ql<=l&&r<=qr) return make_pair(mx[u],mxp[u]);
int m = l+r>>1;
pair<int,int>r1 = qry(lc[u],l,m,ql,qr) , r2 = qry(rc[u],m+1,r,ql,qr);
if(r1.first >= r2.first) return r1;
else return r2;
}
int f[maxn],g[maxn],ps[maxn];
void dfs(int u){
for(int v:G[u]){
dfs(v),merge(rt[u],rt[u],rt[v]);
if(!ps[u]) ps[u] = ps[v];
}
}
int ans;
void dfs2(int u){
ans = max(ans , f[u]);
for(int v:G[u]){
if(!u || qry(rt[g[u]],1,n,ps[v]-len[v]+len[g[u]],ps[v]-1).first)
f[v] = f[u] + 1 , g[v] = v;
else
f[v] = f[u] , g[v] = g[u];
dfs2(v);
}
}
char End;
int main(){
scanf("%d",&n);
scanf("%s",S+1);
fa[0] = -1;
rep(i,1,n) ins(S[i]-'a') , last = tr[last][S[i] - 'a'] , ps[last] = i;
rep(i,1,tot){
G[fa[i]].push_back(i);
if(ps[i]) ins(rt[i] , 1 , n , ps[i]);
}
dfs(0);
dfs2(0);
printf("%d\n",ans);
}
树上修改一个点,求一个点到根的值中和当前点的异或最大值。
区间永久化标记打在 t r i e trie trie树上即可。
可个锤子
发现 n log 2 n n\log^2n nlog2n会 M L E MLE MLE
可以把所有操作和询问都离线在线段树的节点上,
分别给每个节点统一处理就可以做到 O ( n log n ) O(n\log n) O(nlogn)的内存。
A C C o d e \mathcal AC\ Code AC Code
#include
#define maxn 100005
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define lc u<<1
#define rc lc|1
#define pb push_back
#define maxp maxn * 16 * 13
using namespace std;
char Start;
int n,m,fa[maxn],st[maxn],ed[maxn],tim,v[maxn],ans[maxn],isa[maxn];
vector<int>G[maxn];
int ch[maxp][2],tot,sz[maxp];
stack<int>bin;
struct opt{
int w,t;
opt(const int &w=0,const int &t=0):w(w),t(t){
}
};
vector<opt>Q[maxn<<2];
void dfs(int u){
st[u] = ++tim;
rep(i,0,G[u].size()-1)
dfs(G[u][i]);
ed[u] = tim;
}
void ins(int u,int l,int r,int ql,int qr,int v,int t){
if(l>qr||ql>r||ql>qr)return;
if(ql<=l&&r<=qr) return (void)(Q[u].pb(opt(v,t)));
int m = l+r>>1;
ins(lc,l,m,ql,qr,v,t) , ins(rc,m+1,r,ql,qr,v,t);
}
void qry(int u,int l,int r,int p,int v,int id){
Q[u].pb(opt(-v,id));
if(l==r) return;
int m = l+r >> 1;
p <= m ? qry(lc,l,m,p,v,id) : qry(rc,m+1,r,p,v,id);
}
char End;
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&m);
rep(i,2,n){
scanf("%d",&fa[i]);
G[fa[i]].push_back(i);
}
dfs(1);
rep(i,1,n) scanf("%d",&v[i]),ins(1,1,n,st[i],ed[i],v[i],1);
for(int op,u,p,i=1;i<=m;i++){
scanf("%d%d",&op,&u);
if(op == 0){
isa[i] = 0;
scanf("%d",&p);
ins(1,1,n,st[u],ed[u],v[u],-1);
ins(1,1,n,st[u],ed[u],v[u]=p,1);
}
else{
isa[i] = 1;
qry(1,1,n,st[u],v[u],i);
}
}
rep(i,1,n<<2) if(!Q[i].empty()){
rep(i,1,tot) sz[i] = 0 , ch[i][0] = ch[i][1] = 0;
tot = 1;
rep(j,0,Q[i].size()-1){
int u = 1;
if(Q[i][j].w >= 0){
per(d,29,0){
int p = Q[i][j].w >> d & 1;
if(!ch[u][p]) ch[u][p] = ++tot;
u = ch[u][p];
sz[u] += Q[i][j].t;
}
}
else{
Q[i][j].w = -Q[i][j].w;
int r = 0;
per(d,29,0){
int p = Q[i][j].w >> d & 1;
if(sz[ch[u][!p]]) u = ch[u][!p] , r |= 1 << d;
else u = ch[u][p];
}
ans[Q[i][j].t] = max(ans[Q[i][j].t] , r);
}
}
Q[i].clear();
}
rep(i,1,m) if(isa[i]) printf("%d\n",ans[i]),ans[i] = 0;
tim = 0;
rep(i,1,n) G[i].clear();
}
}
给出一个 2 × n 2\times n 2×n的网格,每个格子上有一个字符,再给出一个长度为 k k k的字符串,问不重复经过点的网格游走有多少种方案可以走出这个字符串。
发现只有开头结尾可能会有掉头的走法。
简单哈希简单 d p dp dp即可。
毒瘤去重
#include
#define maxn 4003
#define mod 1000000007
#define md 998244353
#define LL long long
#define S 131
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char s[2][maxn],t[maxn];
int n,K;
LL hs[2][2][maxn],pw[maxn],hst[maxn];
int f[2][2][maxn];
template<class T,class T2>void add(T &u,T2 v){
((u += v) >= mod) && (u -= mod);
}
LL ans = 0;
void Solve(){
int now = 1 , pre = 0;
memset(f,0,sizeof f);
rep(i,1,n){
swap(now,pre);
memset(f[now],0,sizeof f[now]);
rep(j,0,1){
if(s[j][i] == t[1]) f[now][j][1]++;
rep(k,1,min(i,K/2)) if(
(((hs[j^1][1][i-k+1] - hs[j^1][1][i+1] * pw[k]) % md * pw[k]
+ (hs[j][0][i] - hs[j][0][i-k] * pw[k])) % md + md) % md
==
hst[2*k]){
f[now][j][2*k]++;
}
rep(k,2,K){
if(s[j][i] == t[k]) add(f[now][j][k] , f[pre][j][k-1]);
if(s[j][i] == t[k] && s[j^1][i] == t[k-1]) add(f[now][j][k] , f[pre][j^1][k-2]);
}
rep(k,2,min(K/2,n-i)) if(
(((hs[j][0][i+k] - hs[j][0][i] * pw[k]) % md * pw[k]
+(hs[j^1][1][i+1] - hs[j^1][1][i+k+1] * pw[k])) % md + md) % md
==
((hst[K] - hst[K-2*k] * pw[2*k]) % md + md) % md){
add(ans , f[now][j][K-2*k]);
}
add(ans,f[now][j][K]);
}
}
}
int main(){
scanf("%s%s%s",s[0]+1,s[1]+1,t+1);
n = strlen(s[0] + 1);
K = strlen(t + 1);
pw[0] = 1;
rep(i,1,2 * max(n,K)) pw[i] = S * pw[i-1] % md;
rep(t,0,1) rep(i,1,n) hs[t][0][i] = (hs[t][0][i-1] * S + s[t][i]) % md;
rep(t,0,1) per(i,n,1) hs[t][1][i] = (hs[t][1][i+1] * S + s[t][i]) % md;
rep(i,1,K) hst[i] = (hst[i-1] * S + t[i]) % md;
Solve();
reverse(s[0]+1,s[0]+n+1);
reverse(s[1]+1,s[1]+n+1);
rep(t,0,1) rep(i,1,n) hs[t][0][i] = (hs[t][0][i-1] * S + s[t][i]) % md;
rep(t,0,1) per(i,n,1) hs[t][1][i] = (hs[t][1][i+1] * S + s[t][i]) % md;
Solve();
if(K == 1) ans /= 2;
if(K == 2){
rep(i,1,n){
if(s[0][i] == t[1] && s[1][i] == t[2])
ans --;
if(s[0][i] == t[2] && s[1][i] == t[1])
ans --;
}
}
//if(s[0][1] == 'w' && s[0][2] == 'g' && s[0][3] == 'w' && s[0][4] == 'g'){
// puts(t+1);
///}
printf("%lld\n",ans);
}
t r i e trie trie树上建回文树, O ( n log n ) O(n\log n) O(nlogn)
C o d e \mathcal Code Code
#pragma comment(linker, "/STACK:102400000,102400000")
#include
#include
#include
#include
#define maxn 2000006
#define LL long long
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
#define maxc 4
int n;
int tr[maxn][maxc],fa[maxn],len[maxn],dif[maxn],anc[maxn],pos[maxn],tot;
LL sl[maxn],ans;
int info[maxn],Prev[maxn],to[maxn],cst[maxn],cnt_e;
void Node(int u,int v,int w){
Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=w; }
char s[maxn];
void ins(int last,int c,int n){
int p = -1;
while(s[n-len[last]-1] != s[n]){
if(p != dif[last])p = dif[last],last = fa[last];
else last = fa[anc[last]];
}
if(!tr[last][c]){
int u = fa[last]; p = -1;
while(s[n-len[u]-1] != s[n]){
if(p != dif[u]) p = dif[u],u=fa[u];
else u=fa[anc[u]];
}
fa[++tot] = tr[u][c];
len[tot] = len[last] + 2;
sl[tot] = sl[fa[tot]] + len[tot];
dif[tot] = len[tot] - len[fa[tot]];
anc[tot] = (dif[tot] == dif[fa[tot]] ? anc[fa[tot]] : fa[tot]);
tr[last][c] = tot;
}
}
void dfs(int u,int d){
for(int i=info[u];i;i=Prev[i]){
int v = to[i] , w = cst[i];
s[d] = w;
ins(pos[u] , w , d);
pos[v] = tot;
ans += sl[tot];
dfs(v,d+1);
}
}
int main(){
// freopen("1.in","r",stdin);
int T;
for(scanf("%d",&T);T--;){
scanf("%d",&n);
tot = 1;
anc[0] = fa[1] = anc[1] = fa[0] = 1,len[1] = -1;
dif[0] = 1;
rep(i,1,n){
char ch;
while(!isalpha(ch=getchar()));
int u;
scanf("%d",&u);
Node(u,i,ch-'a');
}
s[0] = '#';
dfs(0,1);
printf("%lld\n",ans);
ans = 0;
rep(i,0,n) info[i] = 0;
cnt_e = 0;
memset(tr,0,sizeof(tr[0])*(tot+1));
tot = 0;
}
}
将 1 1 1到 n n n的排列按字典序连接为一个序列,序列长度为 n × n ! n\times n! n×n!,求这个序列不同的连续子序列个数。 1 ≤ n ≤ 1 e 6 1\leq n\leq 1e6 1≤n≤1e6,答案对 998244353 998244353 998244353取模。
直接搜题解
结论题,嗯。
A C C o d e \mathcal AC \ Code AC Code
#include
#define LL long long
#define M 998244353
using namespace std;
LL n,x, a=0;
int main() {
cin >> n; x=n;
for (LL i=0; i<n-1; ++i) {
a += ((x*(n-i-1))%M)*(i+1) + M-x; a%=M;
x = (x*(n-i))%M;
}
LL ans = x*(x-n+2)-2*a + M; ans%=M;
if (ans&1) ans = (ans+M)/2;
else ans /= 2;
cout << ans << endl;
}
回忆树超级加强版