The 2021 ICPC Asia Shanghai & Shenyang Regional Contest

赛前练习。

WA/RE/TLE原因总结:

2021上海

G:inv[0]未赋值为1

J(me):for循环里特判nw=0后直接continue,但实际上还需要有一步修改

B(me):vector 数组下标从 0 开始;容斥取模要注意负数;因为用 vector 存多项式系数所以注意调用的时候不要下标超限;NTT数组最好开成4倍

2021沈阳

J:cin字符数组出错(自写可速A)

L(me):NTT数组开小了(正解直接DP,要分析好复杂度)

B:最后一次WA是因为数组开小了

M(me):一开始没处理q->nq改道的情况,后来没处理nw=q的话改道时要改nw的情况

2021南京

M:特判n=1

H(me):DP时漏了一种max情况(mx1+mx2,当二者不能同时选时,只考虑了nxtmx1+mx2,没考虑mx1+nxtmx2)

D:思路有不完善之处,新加的数作为最大值时的情况算错了

2020银川

E:签到题,但第一发漏了一种情况

J:数组大小写错

G(me):读错题了(虽然能解释样例,最终做法也差不多)

上海站

[B]

分析:

首先要转化问题。从边和回路的角度考虑很巧妙,也容易想到容斥计数。之后问题就是计算选 i 条“禁边”的方案数。熟悉生成函数的话挺容易列出式子的;因为是多个小多项式相乘,所以可以用 NTT 加速,同时用分治。为了做分治,用 vector 或者结构体包一个 vector 来存多项式(系数)。
写完后又 WA, T 了几次,找出几个写错的地方:vector 数组下标从 0 开始,容斥取模要注意负数,因为用 vector 存多项式系数所以注意调用的时候不要下标超限。然后还是一直 WA 第 15 个点,对拍也拍不出错,一晚上没调出来心力交瘁。但是后来做了沈阳站的 L ,写 NTT 出了同样的错误,终于发现是 NTT 用到的一些数组下标超限了(空间开小了)。改了以后终于过了T_T。分治 NTT 因为每次都重新计算 lim,所以要格外小心下标这些。
但是还挺奇怪,按理说开 \(2n=2*10^5\) 大小就足够了,结果 \(数组大小 \leq 2*10^5+30000\) 会 WA, \(2*10^5+30000 \leq 数组大小 \leq 2*10^5+60000\) 会 RE, \(数组大小 \geq 2*10^5+70000\) 才能过……

代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define Nd vector<int>
int const N=3e5+5,mod=998244353;    //!! (2e5会WA)
int n,p[N],siz[N],cnt,rev[N],f[N],g[N];
bool vis[N];
ll fac[N],inv[N];
ll pw(ll x,int y)
{
    ll ret=1;
    while(y)
    {
        if(y&1) ret = ret*x%mod;
        x = x*x%mod;
        y>>=1;
    }
    return ret;
}
void Init()
{
    fac[0]=1; inv[0]=1;
    for(int i=1;i<=n;i++) fac[i] = fac[i-1]*i%mod;
    inv[n] = pw(fac[n],mod-2);
    for(int i=n-1;i;i--) inv[i] = inv[i+1]*(i+1)%mod;
}
ll C(int n,int m){return (n>=m)? (fac[n]*inv[m]%mod)*inv[n-m]%mod : 0;}
void dfs(int u)
{
    if(vis[u])return;
    vis[u]=1; siz[cnt]++;
    dfs(p[u]);
}
int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
void ntt(int *a,int tp,int lim)
{
    for(int i=0;i<lim;i++)
        if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int mid=1;mid<lim;mid<<=1)
    {
        int wn=pw(3,tp==1?(mod-1)/(mid<<1):(mod-1)-(mod-1)/(mid<<1));
        for(int j=0,len=(mid<<1);j<lim;j+=len)
            for(int k=0,w=1;k<mid;k++,w=(ll)w*wn%mod)
            {
                int x=a[j+k],y=(ll)w*a[j+mid+k]%mod;
                a[j+k]=upt(x+y); a[j+mid+k]=upt(x-y);
            }
    }
    if(tp==1)return; int inv=pw(lim,mod-2);
    for(int i=0;i<lim;i++)a[i]=(ll)a[i]*inv%mod;
}
/*
struct Nd{
    vector<int> a;
    friend Nd operator * (Nd x, Nd y)
    {
        puts("in*");
        int lim=1; int L=0; int tot=x.a.size()+y.a.size();
        while(lim<=tot) lim<<=1, L++;
        printf("lim=%d\n",lim);
        for(int i=0;i<lim;i++) rev[i]=((rev[i>>1]>>1)|((i&1)<<(L-1)));
        // x.a.resize(lim); y.a.resize(lim);
        for(int i=0;i<lim;i++) f[i]=(i<x.a.size())?x.a[i]:0;
        for(int i=0;i<lim;i++) g[i]=(i<y.a.size())?y.a[i]:0;

        ntt(f,1,lim); ntt(g,1,lim);
        for(int i=0;i<lim;i++) f[i]=(ll)f[i]*g[i]%mod;
        ntt(f,-1,lim);

        x.a.clear();
        for(int i=0;i<=tot;i++)x.a.push_back(f[i]);
        return x;
    }
};
*/
vector< Nd > Pol;
Nd mul(Nd x,Nd y)
{
    // puts("in*");
    int lim=1; int L=0; int tot=x.size()+y.size();
    while(lim<=tot) lim<<=1, L++;
    // printf("lim=%d\n",lim);
    for(int i=0;i<lim;i++) rev[i]=((rev[i>>1]>>1)|((i&1)<<(L-1)));
    for(int i=0;i<lim;i++) f[i]=(i<x.size())?x[i]:0;
    for(int i=0;i<lim;i++) g[i]=(i<y.size())?y[i]:0;

    ntt(f,1,lim); ntt(g,1,lim);
    for(int i=0;i<lim;i++) f[i]=(ll)f[i]*g[i]%mod;
    ntt(f,-1,lim);

    Nd ret;
    for(int i=0;i<=tot;i++)ret.push_back(f[i]);
    return ret;
}
Nd work(int l,int r)
{
    // printf("l=%d r=%d\n",l,r);
    if(l==r) return Pol[l-1]; // 注意Pol的下标从0开始T_T
    int mid=((l+r)>>1);
    // puts("!!");
    return mul(work(l,mid),work(mid+1,r));
}
void prt(Nd x)
{
    printf("len=%d: ",x.size());
    for(int i=0;i<x.size();i++)printf("%d ",x[i]); puts("");
}
int main()
{
    // freopen("data.txt","r",stdin);
    scanf("%d",&n); Init();
    for(int i=1;i<=n;i++) scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)
        if(!vis[i])cnt++,dfs(i);

    for(int i=1;i<=cnt;i++)
    {
        // printf("i=%d\n",i);
        Nd tmp; tmp.push_back(1);
        for(int j=1;j<siz[i];j++)
            tmp.push_back(C(siz[i],j));
        Pol.push_back(tmp);
        // prt(tmp);
    }

    Nd coe = work(1,cnt);
    // prt(coe);
    ll ans=0;
    for(int i=0,f=1;i<=min((ll)n,(ll)coe.size()-1);i++,f*=(-1)) // min!!
        ans = ((ans + (ll)f*coe[i]*fac[n-i]%mod)%mod+mod)%mod; // 注意负数!
    printf("%lld\n",ans);
    return 0;
}

[J]

分析:

把前缀和 s 排序后按这个顺序枚举,能够满足前面出现的位置都是 s 更大的,这样做挺巧妙。
积累过 bitset 的用法,却没有真正用过。这次算是实践了。

代码如下
#include<bits/stdc++.h>
using namespace std;
int const N=5e4+5;
int n;
struct Nd{
	int sum,pos;
}s[N];
char A[N],B[N];
bitset<N> C,V,Tmp;
bool cmp(Nd a,Nd b) {return (a.sum==b.sum)?(a.pos<b.pos):(a.sum>b.sum);}
void prt()
{
	printf("C:");
	for(int i=1;i<=n;i++) cout<<C[i];
	puts("");
}
int main()
{
	int T; scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		cin>>A+1>>B+1;
		s[0].sum=0;  s[0].pos=0;
		for(int i=1;i<=n;i++) 
			s[i].sum = s[i-1].sum + ((A[i]=='0')?(-1):1),  s[i].pos=i;
		sort(s,s+n+1,cmp); // 含s[0]
		// for(int i=0;i<=n;i++) printf("s[%d]=(%d,%d)\n",i,s[i].sum,s[i].pos);

		C.reset();  V.reset();	int lst=N+1;// C=1表示不合法
		Tmp.reset(); for(int i=1;i<=n;i++) Tmp.set(i,1);
		for(int i=0,nw;i<=n;i++) // on B[s[i].pos] !!
		{
			nw = s[i].pos;
			// if(nw==0){V.set(n,1); continue;} //!!!
			if(B[nw]=='1') C |= (V>>(n-nw));
			// else C |= ((~V) >> (n-nw));
			else C |= ((V^Tmp) >> (n-nw));
			if((B[nw]=='1'&&s[i].sum<=0) || (B[nw]=='0'&&s[i].sum>0)) lst=min(lst,nw); // 在nw处nw不合法,则大于nw的都不合法
			V.set(n-nw,1);
			// printf("i=%d nw=%d\n",i,nw); prt();
		}

		for(int i=1;i<=n;i++)
		{
			// printf("i=%d\n",i);
			if(i>=lst)putchar('0');
			else cout<<(C[i]^1);
		}
		puts("");
	}
	return 0;
}

沈阳站

[J]

分析:

很快就有思路了,先差分,然后可以想到每个位置都是一次做到终态的,因为如果反复加减的话,说明后面有位置需要它这样来附带一些加减次数,但这还不如后面那个位置自己做这些次数的加减,答案只可能更优。所以直接 dfs 每个位置是加到终态还是减到终态就行了。
跟队友说了以后,他们似乎理解成另一种做法,先处理再差分;本质上没区别。但是写得有点复杂(而且是 bfs),然后不知哪里写挂了一直 RE 和 WA。最后时刻把 cin 字符数组改成字符就过了。比较奇怪。
下来自己又写了一下我原来的想法,很简单,很快就过了。看来以后有把握的题自己能写就写。

代码如下
#include<bits/stdc++.h>
using namespace std;
char A[10],B[10];
int a[10],b[10],ans;
int upt(int x){return (x%10+10)%10;}
void dfs(int t,int add,int mns,int use)
{
	if(t==5) {ans=min(ans,use); return;}

	if(a[t]<=add)dfs(t+1,add-a[t],mns,use); // use add
	else dfs(t+1,0,mns+a[t]-add,use+a[t]-add);

	if(10-a[t]<=mns)dfs(t+1,add,mns-(10-a[t]),use);
	else dfs(t+1,add+(10-a[t])-mns,0,use+(10-a[t])-mns);
}
int main()
{
	int T; scanf("%d",&T);
	while(T--)
	{
		scanf("%s%s",A+1,B+1); ans=0x3f3f3f3f;
		for(int i=1;i<=4;i++)
			a[i]=(int)(A[i]-'0'), b[i]=(int)(B[i]-'0');
		for(int i=4;i>1;i--) a[i]-=a[i-1], b[i]-=b[i-1];
		for(int i=1;i<=4;i++) a[i]=upt(b[i]-a[i]);
		dfs(1,0,0,0);
		printf("%d\n",ans);
	}
	return 0;
}

[L]

分析:

容斥+树形DP。但是一开始想 DP 是\(O(n^3)\)的,于是想用 NTT 优化成\(O(n^2logn)\),但是还是 T 了。
后来看题解,发现没有注意到转移时只需要枚举到 siz[u]/2 或者 siz[v]/2;而这样枚举的话直接 DP 即可,因为考虑它枚举的过程,会发现大约等价于枚举树上所有的两个点的点对(说大约是因为这里 siz 还除以 2 了),也就是复杂度总体是 \(O(n^2)\) 的。这个想法之前见过,感觉很妙,这里就用上了。
还试图这样减小 NTT 每轮的 \(lim\) ,结果一直 WA 在第 8 个点,百思不得其解。后来调来调去,发现 \(lim \geq siz[u]+siz[v]\) 会 WA,但是改成 \(lim \geq siz[u]/2\) 就是意料之中的 T 了。感觉是因为随意增大 \(lim\) 会出问题,下标超限什么的(比如最后一步这样求和已经大于 \(n\) 了)。以后写 NTT 时得注意精确地赋值 \(lim\)

代码如下
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int const N=4005,mod=998244353,xn=(1<<13);
int n,fa[N],hd[N],to[N<<1],nxt[N<<1],cnt,siz[N];
int rev[xn];
ll A[xn],B[xn];
ll dp[N][N][3],fac[N],inv[N],inv2[N];
int upt(ll x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
ll pw(ll x,int y)
{
    ll ret=1;
    for(;y;y>>=1,x=(x*x)%mod) if(y&1)ret=(ret*x)%mod;
    return ret;
}
void Init()
{
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod;
    inv[n]=pw(fac[n],mod-2);
    for(int i=n-1;i>=0;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
    int nw=1;
    for(int i=1;i<=n;i++) nw=upt(nw*2);
    inv2[n]=pw(nw,mod-2);
    for(int i=n-1;i>=0;i--) inv2[i]=upt(inv2[i+1]*2);
}
void ntt(ll *a,int tp,int lim)
{
    for(int i=0;i<lim;i++)
    if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int mid=1;mid<lim;mid<<=1)
    {
        int wn=pw(3,tp==1?(mod-1)/(mid<<1):(mod-1)-(mod-1)/(mid<<1));
        for(int j=0,len=(mid<<1);j<lim;j+=len)
        for(int k=0,w=1;k<mid;k++,w=(ll)w*wn%mod)
        {
            int x=a[j+k],y=(ll)w*a[j+mid+k]%mod;
            a[j+k]=upt(x+y); a[j+mid+k]=upt(x-y);
        }
    }
    if(tp==1)return; int inv=pw(lim,mod-2);
    for(int i=0;i<lim;i++)a[i]=(ll)a[i]*inv%mod;
}
void add(int x,int y){to[++cnt]=y; nxt[cnt]=hd[x]; hd[x]=cnt;}
ll g[xn][3];
void dfs(int u)
{
    // printf("u=%d\n",u);
    dp[u][0][0]=1; siz[u]=1;
    for(int i=hd[u],v;i;i=nxt[i])
    {
        if((v=to[i])==fa[u])continue;
        fa[v]=u; dfs(v); siz[u]+=siz[v];
        int lim=1; int L=0;
        while(lim<=siz[u]/2) lim<<=1,L++; // siz[u]/2 (若写成siz[u]+siz[v]则WA on test8)
        for(int i=0;i<lim;i++) rev[i]=((rev[i>>1]>>1)|((i&1)<<(L-1)));

        // TLE
        // memset(g,0,sizeof g);
        // for(int j=0;j<=siz[u]/2;j++)
        //     for(int k=0;k<=j&&k<=siz[v]/2;k++)
        //         g[j][0]=(g[j][0]+dp[u][j-k][1]*(dp[v][k][0]+dp[v][k][1]))%mod;
        // for(int j=0;j<=siz[u]/2;j++) dp[u][j][1]=g[j][0];

        // for(int j=0;j<=siz[u]/2;j++)
        //     for(int k=0;k<=j&&k<=siz[v]/2;k++)
        //         g[j][1]=(g[j][1]+dp[u][j-k][0]*dp[v][k][0])%mod;
        // for(int j=1;j<=siz[u]/2;j++) dp[u][j][1]=upt(dp[u][j][1]+g[j-1][1]);

        // for(int j=0;j<=siz[u]/2;j++)
        //     for(int k=0;k<=j&&k<=siz[v]/2;k++)
        //         g[j][2]=(g[j][2]+dp[u][j-k][0]*(dp[v][k][0]+dp[v][k][1]))%mod;
        // for(int j=0;j<=siz[u]/2;j++) dp[u][j][0]=g[j][2];

        //约等价于上面写法,TLE,无法避免的多余时间在于dp[v]可转移的仅有siz[v]/2个,而这里扩充成siz[u]/2个
        for(int j=0;j<lim;j++) A[j]=dp[u][j][1], B[j]=upt(dp[v][j][0]+dp[v][j][1]);
        ntt(A,1,lim); ntt(B,1,lim);
        for(int j=0;j<lim;j++) A[j]=(ll)A[j]*B[j]%mod;
        ntt(A,-1,lim);
        for(int k=0;k<lim;k++) dp[u][k][1]=A[k];

        for(int j=0;j<lim;j++) A[j]=dp[u][j][0], B[j]=dp[v][j][0];
        ntt(A,1,lim); ntt(B,1,lim);
        for(int j=0;j<lim;j++) A[j]=(ll)A[j]*B[j]%mod;
        ntt(A,-1,lim);
        for(int k=1;k<=lim;k++) dp[u][k][1]=upt(dp[u][k][1]+A[k-1]);

        for(int j=0;j<lim;j++) A[j]=dp[u][j][0], B[j]=upt(dp[v][j][0]+dp[v][j][1]);
        ntt(A,1,lim); ntt(B,1,lim);
        for(int j=0;j<lim;j++) A[j]=(ll)A[j]*B[j]%mod;
        ntt(A,-1,lim);
        for(int k=0;k<lim;k++) dp[u][k][0]=A[k];
    }
}
void dfs2(int u)
{
    dp[u][0][0]=1; siz[u]=1;
    for(int i=hd[u],v;i;i=nxt[i])
    {
        if((v=to[i])==fa[u])continue;
        fa[v]=u; dfs2(v); 
        /* TLE
        siz[u]+=siz[v];
        memset(g,0,sizeof(g));
        for(int j=0;j<=siz[u]/2;j++) 
        {
            g[j][0]=dp[u][j][0], g[j][1]=dp[u][j][1];
            dp[u][j][0]=0; dp[u][j][1]=0;
        }
        for(int k=0;k<=siz[u]/2;k++)
        {
            for(int j=0;j<=siz[v]/2&&j<=k;j++)
            {
                // dp[u][k][0]=upt(dp[u][k][0] + g[k-j][0]*(dp[v][j][0]+dp[v][j][1])%mod);
                // dp[u][k][1]=upt(dp[u][k][1] + g[k-j][1]*(dp[v][j][0]+dp[v][j][1])%mod);
                // if(k-1-j>=0)dp[u][k][1]=upt(dp[u][k][1] + g[k-1-j][0]*dp[v][j][0]%mod);
                dp[u][k][0] += g[k-j][0]*(dp[v][j][0]+dp[v][j][1]);
                dp[u][k][1] += g[k-j][1]*(dp[v][j][0]+dp[v][j][1]);
                if(k-1-j>=0) dp[u][k][1] += g[k-1-j][0]*dp[v][j][0];
                dp[u][k][0]%=mod; dp[u][k][1]%=mod;
            }
        }
        */
        
        //(AC) 此做法的复杂度为n^2,相当于枚举点对
        memset(g,0,sizeof g);
        for(int j=0;j<=siz[u]/2;j++){
            for(int k=0;k<=siz[v]/2;k++){
                g[j+k][0] += dp[u][j][0]*(dp[v][k][0]+dp[v][k][1]);
                g[j+k][1] += dp[u][j][1]*(dp[v][k][0]+dp[v][k][1]);
                g[j+k+1][1] += dp[u][j][0]*dp[v][k][0];
                g[j+k][0] %= mod,g[j+k][1] %= mod,g[j+k+1][1] %= mod;
            }
        }
        siz[u] = siz[u] + siz[v];
        for(int j=0;j<=siz[u]/2+1;j++)
            dp[u][j][0] = g[j][0],dp[u][j][1] = g[j][1];
    }
}
int cal(int m){return ((ll)fac[m]*inv[m/2]%mod)*(ll)inv2[m/2]%mod;}
ll C(int n,int m){return (ll)fac[n]*fac[m]%mod*fac[n-m]%mod;}
int main()
{
    // freopen("data2.txt","r",stdin);
    scanf("%d",&n); n<<=1; //n个点
    Init();
    for(int i=1,u,v;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v); add(v,u);
    }
    dfs2(1);
    // for(int k=0;k<n;k++)printf("dp[1][%d][0]=%lld, dp[1][%d][1]=%lld\n",k,dp[1][k][0],k,dp[1][k][1]);

    ll ans=0;
    for(int i=0,f=1;i<=n;i++,f*=(-1))
        ans = ((ans + (ll)f*(dp[1][i][0]+dp[1][i][1])%mod*(ll)cal(n-2*i)%mod)%mod+mod)%mod;
    printf("%lld\n",ans);

    return 0;
}
/*
2
1 2
1 3
3 4

3
1 2
2 3
3 4
4 5
5 6
*/

[M]

分析:

按题解标算试着写了一下,但是不成功。
试了样例1,问题在于从 \(potat\)\(potato\) 这一步,原来的最大串 \(tat\) 在后缀自动机上是一条路径 \(1-4(t)-5(a)-6(t)\) 。但是结束后新增了一个 \(7\) 号点,原来从 \(1\) 号点走 \(t\) 直接到的是 \(4\) 号点,现在变成 \(7\) 号点了。然后再加入字符 \(o\) ,新增 \(o\) 边的是 \(6\) 号点和 \(7\) 号点,没有办法回退到根再走出一个 \(to\) 来。
不知道怎么解决,先放在这里……

代码如下
#include<bits/stdc++.h>
using namespace std;
int const N=1e6+5;
int n,lst=1,cnt=1,go[N][30],fa[N],l[N],a[N],dep[N],son[N],nw,pre[N];
char s[N];
void add(int w)
{
	printf("W=%d\n",w);
	for(int i=1;i<=cnt;i++)printf("dep[%d]=%d pre[%d]=%d ",i,dep[i],i,pre[i]); puts("");
	int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
	int fre=nw;
	for(;p&&!go[p][w];p=fa[p])
	{
		go[p][w]=np;
		if(w==14)printf("%d -> %d\n",p,np);
		printf("dep[%d]=%d dep[fre=%d]=%d\n",p,dep[p],fre,dep[fre]);
		if(dep[p] && son[p]<(1<<w) && dep[p]<dep[fre]) fre=p; // 只有在路径上的点dep!=0
		son[p] |= (1<<w);
	}
	printf("fre=%d nw=%d\n",fre,nw);
	if(fre>-1)
		while(nw!=fre) dep[nw]=0, nw=pre[nw]; // 回退
	printf("after: fre=%d nw=%d\n",fre,nw);

	if(!p) fa[np]=1;
	else
	{
		int q=go[p][w];
		if(l[q]==l[p]+1) fa[np]=q;
		else
		{
			int nq=++cnt; l[nq]=l[p]+1;
			fa[nq]=fa[q]; fa[q]=nq; fa[np]=nq;
			memcpy(go[nq],go[q],sizeof go[q]); son[nq]=son[q];
			for(;go[p][w]==q;p=fa[p]) go[p][w]=nq;
		}
	}
	printf("GO: %d -> %d\n",nw,go[nw][w]);
	dep[go[nw][w]]=dep[nw]+1; pre[go[nw][w]]=nw; nw=go[nw][w]; // 新走一步
}
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	nw=1; dep[1]=1;
	for(int i=1;i<=n;i++)
	{
		add(s[i]-'a');
		printf("nw=%d\n",nw);
		printf("%d %d\n",i-dep[nw]+2,i);
	}
	return 0;
}

更新:解决上面那个情况,只要路径改道就可以了!
改完以后样例能过了,但是第4个点TLE了;又加了个 \(nxt\) 数组小小优化一下,加了快写,还是T,真是没办法了……

代码如下
#include<bits/stdc++.h>
using namespace std;
int const N=1e6+5;
int n,lst=1,cnt=1,go[N][30],fa[N],l[N],a[N],dep[N],son[N],nw,pre[N],nxt[N];
char s[N];
void add(int w)
{
	// printf("W=%d\n",w);
	// for(int i=1;i<=cnt;i++)printf("dep[%d]=%d pre[%d]=%d ",i,dep[i],i,pre[i]); puts("");
	int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
	int fre=nw;
	for(;p&&!go[p][w];p=fa[p])
	{
		go[p][w]=np;
		// if(w==14)printf("%d -> %d\n",p,np);
		// printf("dep[%d]=%d dep[fre=%d]=%d\n",p,dep[p],fre,dep[fre]);
		if(dep[p] && son[p]<(1<<w) && dep[p]<dep[fre]) fre=p; // 只有在路径上的点dep!=0
		son[p] |= (1<<w);
	}
	// printf("fre=%d nw=%d\n",fre,nw);
	if(fre>-1)
		while(nw!=fre) dep[nw]=0, nw=pre[nw], pre[nxt[nw]]=0, nxt[nw]=0; // 回退
	// printf("after: fre=%d nw=%d\n",fre,nw);

	if(!p) fa[np]=1;
	else
	{
		int q=go[p][w];
		if(l[q]==l[p]+1) fa[np]=q;
		else
		{
			int nq=++cnt; l[nq]=l[p]+1;
			fa[nq]=fa[q]; fa[q]=nq; fa[np]=nq;
			memcpy(go[nq],go[q],sizeof go[q]); son[nq]=son[q];
			if(!dep[q]) {for(;go[p][w]==q;p=fa[p]) go[p][w]=nq;} // 如果q不在路径上则不影响
			else // 如果q在路径上,但pre[q]->q没被改成pre[q]->nq,则也不影响
			{
				for(;go[p][w]==q;p=fa[p])
				{
					if(p==pre[q]) // 路径此点需要修改,p->q改成p->nq, q->nxt改成nq->nxt
					{
						dep[nq]=dep[q]; pre[nq]=p; dep[q]=0;
						// for(int x=0;x<26;x++)
						// 	if(go[q][x] && dep[go[q][x]]) {pre[go[q][x]]=nq; break;} // 上述nxt=go[q][x]
						pre[nxt[q]]=nq; nxt[q]=0; pre[q]=0;
					}
					go[p][w]=nq;
				}
			}
		}
	}
	// printf("GO: %d -> %d\n",nw,go[nw][w]);
	dep[go[nw][w]]=dep[nw]+1; pre[go[nw][w]]=nw; nxt[nw]=go[nw][w]; nw=go[nw][w]; // 新走一步
}
void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	nw=1; dep[1]=1;
	for(int i=1;i<=n;i++)
	{
		add(s[i]-'a');
		// printf("nw=%d\n",nw);
		// printf("%d %d\n",i-dep[nw]+2,i);
		write(i-dep[nw]+2); putchar(' '); write(i); putchar('\n');
	}
	return 0;
}

更新2:怀疑是死循环了,在唯一的 \(while\) 循环那里加了个保险退出条件,然后第4个点变成WA了,修理了一下链表部分还是WA,唉……暂时不知怎么改。

代码如下
#include<bits/stdc++.h>
using namespace std;
int const N=1e6+5;
int n,lst=1,cnt=1,go[N][30],fa[N],l[N],a[N],dep[N],son[N],nw,pre[N],nxt[N];
char s[N];
void add(int w)
{
	// printf("W=%d\n",w);
	// for(int i=1;i<=cnt;i++)printf("dep[%d]=%d pre[%d]=%d ",i,dep[i],i,pre[i]); puts("");
	int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
	int fre=-1;
	for(;p&&!go[p][w];p=fa[p])
	{
		go[p][w]=np;
		// if(w==14)printf("%d -> %d\n",p,np);
		// printf("dep[%d]=%d dep[fre=%d]=%d\n",p,dep[p],fre,dep[fre]);
		if(dep[p] && son[p]<(1<<w) && (fre==-1||dep[p]<dep[fre])) fre=p; // 只有在路径上的点dep!=0
		son[p] |= (1<<w);
	}
	// printf("fre=%d nw=%d\n",fre,nw);
	if(fre>-1)
		while(dep[nw]&&nw!=fre) dep[nw]=0, nw=pre[nw], pre[nxt[nw]]=0, nxt[nw]=0; // 回退
	// printf("after: fre=%d nw=%d\n",fre,nw);

	if(!p) fa[np]=1;
	else
	{
		int q=go[p][w];
		if(l[q]==l[p]+1) fa[np]=q;
		else
		{
			int nq=++cnt; l[nq]=l[p]+1;
			fa[nq]=fa[q]; fa[q]=nq; fa[np]=nq;
			memcpy(go[nq],go[q],sizeof go[q]); son[nq]=son[q];
			if(!dep[q]) {for(;go[p][w]==q;p=fa[p]) go[p][w]=nq;} // 如果q不在路径上则不影响
			else // 如果q在路径上,但pre[q]->q没被改成pre[q]->nq,则也不影响
			{
				for(;go[p][w]==q;p=fa[p])
				{
					if(p==pre[q]) // 路径此点需要修改,p->q改成p->nq, q->nxt改成nq->nxt
					{
						dep[nq]=dep[q]; pre[nq]=p; nxt[p]=nq;
						// for(int x=0;x<26;x++)
						// 	if(go[q][x] && dep[go[q][x]]) {pre[go[q][x]]=nq; break;} // 上述nxt=go[q][x]
						pre[nxt[q]]=nq; nxt[nq]=nxt[q];
						nxt[q]=0; pre[q]=0; dep[q]=0;
					}
					go[p][w]=nq;
				}
			}
		}
	}
	// printf("GO: %d -> %d\n",nw,go[nw][w]);
	dep[go[nw][w]]=dep[nw]+1; pre[go[nw][w]]=nw; nxt[nw]=go[nw][w]; nw=go[nw][w]; // 新走一步
}
void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	nw=1; dep[1]=1;
	for(int i=1;i<=n;i++)
	{
		add(s[i]-'a');
		// printf("nw=%d\n",nw);
		// printf("%d %d\n",i-dep[nw]+2,i);
		write(i-dep[nw]+2); putchar(' '); write(i); putchar('\n');
	}
	return 0;
}

更新3:对拍发现了一组错误数据: \(bggotiho\) ,调了调发现是因为涉及到 \(nq\)\(q\) 的路径改道时,还要考虑当前路径终点 \(nw=q\) 的情况,此时要令 \(nw=nq\) 。改了以后还RE了一发,因为数组开小了;两倍字符串大小就过了~

代码如下
#include<bits/stdc++.h>
using namespace std;
int const N=2e6+5;
int n,lst=1,cnt=1,go[N][30],fa[N],l[N],a[N],dep[N],son[N],nw,pre[N],nxt[N];
char s[N];
void add(int w)
{
	// printf("W=%d\n",w);
	// for(int i=1;i<=cnt;i++)printf("dep[%d]=%d pre[%d]=%d ",i,dep[i],i,pre[i]); puts("");
	int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
	int fre=-1;
	for(;p&&!go[p][w];p=fa[p])
	{
		go[p][w]=np;
		// if(w==14)printf("%d -> %d\n",p,np);
		// printf("dep[%d]=%d dep[fre=%d]=%d\n",p,dep[p],fre,dep[fre]);
		if(dep[p] && son[p]<(1<<w) && (fre==-1||dep[p]<dep[fre])) fre=p; // 只有在路径上的点dep!=0
		son[p] |= (1<<w);
	}
	// printf("fre=%d nw=%d\n",fre,nw);
	if(fre>-1){while(nw!=fre) dep[nw]=0, nxt[nw]=0, nw=pre[nw], pre[nxt[nw]]=0, nxt[nw]=0;} // 回退
	// printf("after: fre=%d nw=%d\n",fre,nw);
	// printf("AFTER: "); for(int i=1;i<=cnt;i++)printf("dep[%d]=%d pre[%d]=%d ",i,dep[i],i,pre[i]); puts("");

	if(!p) fa[np]=1;
	else
	{
		int q=go[p][w];
		if(l[q]==l[p]+1) fa[np]=q;
		else
		{
			int nq=++cnt; l[nq]=l[p]+1;
			fa[nq]=fa[q]; fa[q]=nq; fa[np]=nq;
			memcpy(go[nq],go[q],sizeof go[q]); son[nq]=son[q];
			if(!dep[q]) {for(;go[p][w]==q;p=fa[p]) go[p][w]=nq;} // 如果q不在路径上则不影响
			else // 如果q在路径上,但pre[q]->q没被改成pre[q]->nq,则也不影响
			{
				for(;go[p][w]==q;p=fa[p])
				{
					if(p==pre[q]) // 路径此点需要修改,p->q改成p->nq, q->nxt改成nq->nxt
					{
						if(nw==q)nw=nq; //!!!
						// printf("CHANGE: "); printf("%d -> %d -> %d  nq=%d\n",p,q,nxt[q],nq);
						dep[nq]=dep[q]; pre[nq]=p; nxt[p]=nq;
						// for(int x=0;x<26;x++)
						// 	if(go[q][x] && dep[go[q][x]]) {pre[go[q][x]]=nq; break;} // 上述nxt=go[q][x]
						pre[nxt[q]]=nq; nxt[nq]=nxt[q];
						nxt[q]=0; pre[q]=0; dep[q]=0;
					}
					go[p][w]=nq;
				}
			}
		}
	}
	// printf("GO: %d -> %d dep[%d]=%d\n",nw,go[nw][w],nw,dep[nw]);
	// if(fre>-1){while(dep[nw]&&nw!=fre) dep[nw]=0, nxt[nw]=0, nw=pre[nw], pre[nxt[nw]]=0, nxt[nw]=0;} // 回退
	dep[go[nw][w]]=dep[nw]+1; pre[go[nw][w]]=nw; nxt[nw]=go[nw][w]; nw=go[nw][w]; // 新走一步
}
void write(int x)
{
    if(x<0)
        putchar('-'),x=-x;
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
    return;
}
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	nw=1; dep[1]=1;
	for(int i=1;i<=n;i++)
	{
		add(s[i]-'a');
		// printf("cnt=%d nw=%d dep[%d]=%d\n",cnt,nw,nw,dep[nw]);
		// printf("%d %d\n",i-dep[nw]+2,i);
		write(i-dep[nw]+2); putchar(' '); write(i); putchar('\n');
	}
	return 0;
}
/*
bggotiho

1 1
2 2
2 3
4 4
5 5
5 6
5 7
5 8
*/
posted @ 2022-11-02 20:50  Zinn  阅读(38)  评论(0编辑  收藏  举报