#172. 【WC2016】论战捆竹竿
这是一个美好的下午,小 W 和小 C 在竹林里切磋捆竹竿的技艺。
竹林里有无数根完全一样的短竹子,每一根竹子由 nn 节组成。
这些竹子比较特别,每一节都被染上了颜色。可能的颜色一共 2626 种,分别用小写英文字母 a 到 z 表示。也就是说,如果把竹子的底端到顶端的颜色按顺序写出来可以排成一个由小写英文字母组成的字符串。
小 W 和小 C 都是捆竹竿的高手,他们知道怎样才能把零散的短竹子捆成一整根长竹竿。初始时你拿着一根短竹子作为当前的竹竿。每次你可以选择一根短竹子,短竹子底端若干节(可以是 00 节)与竹竿的最上面若干节对应地一节一节捆起来,而短竹子前面剩下的节伸出去,这样就得到了一根更长的竹竿。注意,竹子的底端是靠近根部的那一端,不可以颠倒。
小 W 对竹竿的审美要求很高,他捆竹竿时有一个癖好:如果两根竹子的某两节被捆在了一起,那么它们的颜色必须相同。
我们假设一根短竹子从底端到顶端每节的颜色为 aba。
那么两根竹子可以首尾捆在一起,可以得到一根颜色为 abaaba 的竹竿;也可以将第一根顶端的一节 a 与第二根底端的一节 a 捆在一起,得到一根颜色为 ababa 的竹竿;还可以直接将每一节都对应起来,捆成一根颜色为 aba 的竹竿。
假设我们在颜色为 ababa 的竹竿顶端再捆一根竹子,则可以捆成 ababaaba,abababa 和 ababa 三种不同的情况。
但是小 C 在这个问题上有不同的看法,他认为小 W 捆不出很多种长度不同的竹竿。小 W 非常不服,于是他找到了你——现在请你求出在竹竿长度不超过 ww 的情况下,小 W 可以捆出多少种长度不同的竹竿。其中,竹竿的长度指从底端到顶端的竹子的节的个数。
注意:如果 w<nw<n,则没有合法的长度,此时答案为 00。
输入格式
第一行包含 11 个正整数 TT,为数据组数。
每组数据的第一行包含 22 个正整数 nn 和 ww,表示短竹子的长度和竹竿的长度上限。
每组数据的第二行包含一个长度为 nn 的字符串,该字符串仅由小写英文字母构成,表示短竹子从底端到顶端每节的颜色。
输出格式
输出共 TT 行,每行包含一个整数表示捆成竹竿的不同长度种数。
样例一
input
1 4 11 bbab
output
5
explanation
可以捆成长度不超过 1111 的竹竿有 66 种不同的情况:
- bbab
- bbabbab
- bbabbbab
- bbabbabbab
- bbabbabbbab
- bbabbbabbab
后两种竹竿长度相同,因此不同长度的竹竿共有 55 种。长度分别为:4,7,8,10,114,7,8,10,11。
样例二
input
2 44 1000 baaaaaabaabbaaabbbbabbbaaabbbababaaabaaabaaa 41 1000 abaabbabaaabaabbbbbbbbbbbababbbbaaabaabbb
output
195 24
样例三
见样例数据下载。
限制与约定
对于所有的测试数据,保证所有的字符串均由小写字母构成,保证 T=5T=5。
各测试点满足以下约定:
测试点编号 | nn | ww | 约束 |
---|---|---|---|
1 | ≤10≤10 | ≤10≤10 | ss 仅包含字母 a 和 b |
2 | ≤20≤20 | ≤20≤20 | |
3 | ≤100≤100 | ≤1018≤1018 | 无 |
4 | |||
5 | ≤103≤103 | ||
6 | |||
7 | ≤5×104≤5×104 | ≤105≤105 | |
8 | |||
9 | |||
10 | |||
11 | ≤7×104≤7×104 | ≤1018≤1018 | |
12 | |||
13 | ≤105≤105 | ||
14 | |||
15 | |||
16 | |||
17 | ≤5×105≤5×105 | ||
18 | |||
19 | |||
20 |
时间限制:1s1s
空间限制:256MB
/* 可能是因为string导致7~10爆了空间 */ #include<iostream> #include<cstdio> #include<cstring> #define maxn 500010 using namespace std; int n,w,nxt[maxn],ans; string s; bool vis[maxn]; void getnxt(){ int i=0,j=-1; nxt[0]=-1; while(i!=n){ if(s[i]==s[j]||j==-1)nxt[++i]=++j; else j=nxt[j]; } } void dfs(string c,int len){ if(!vis[len]&&len<=w)vis[len]=1,ans++; else return; int j=nxt[n]; while(j){ dfs(c+s.substr(j,n-j),len+(n-j)); j=nxt[j]; } dfs(c+s,len+n); } int main(){ int T;scanf("%d",&T); while(T--){ memset(vis,0,sizeof(vis));ans=0; scanf("%d%d",&n,&w);cin>>s; getnxt(); dfs(s,n); printf("%d\n",ans); } }
10分 kmp暴力
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; inline int read() { int t=1,sum=0;char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) t=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) sum=sum*10+ch-‘0‘,ch=getchar(); return t*sum; } const int Max=1e6+10; const LL inf=1e18; int nxt[Max],q[Max],Q[Max],len[Max],n,nm; LL f[Max],w[Max],W; char s[Max]; void get_nxt() { int i,j; for(nxt[1]=j=0,i=2;i<=n;i++) { while(j&&s[j+1]!=s[i]) j=nxt[j]; nxt[i]=s[j+1]==s[i]?++j:j; } } int gcd(int a,int b) { return !b?a:gcd(b,a%b); } void change(int x) { int d=gcd(x,nm),i,j,tot; static LL pre[Max]; for(i=0;i<nm;i++) pre[i]=f[i]; for(i=0;i<x;i++) f[i]=inf; for(i=0;i<nm;i++) if(pre[i]!=inf) j=pre[i]%x,f[j]=min(f[j],pre[i]); for(i=0;i<d;i++) { tot=0; j=i; while(true) { q[++tot]=j; j=(j+nm)%x; if(j==i) break; } for(j=1;j<=tot;j++) q[tot+j]=q[j]; tot<<=1; for(j=2;j<=tot;j++) f[q[j]]=min(f[q[j]],f[q[j-1]]+nm); } nm=x; } void update(int a0,int d,int num) { int D=gcd(d,a0),i,j,k,tot,N,l,r; static LL w[Max],mi; change(a0); for(i=0;i<D;i++) { tot=0; j=i; while(true) { q[++tot]=j; j=(j+d)%a0; if(j==i) break; } for(mi=inf,j=1;j<=tot;j++) if(f[q[j]]<mi) mi=f[q[j]],k=j; if(mi==inf) continue; else N=0; for(j=k;j<=tot;j++) Q[++N]=q[j]; for(j=1;j<k;j++) Q[++N]=q[j]; q[1]=l=r=1; w[1]=f[Q[1]]-d; for(j=2;j<=N;j++) { if(q[l]+num<=j) l++; if(l<=r) f[Q[j]]=min(f[Q[j]],w[l]+1LL*j*d+a0); while(l<=r&&w[r]>=f[Q[j]]-1LL*j*d) r--; q[++r]=j; w[r]=f[Q[j]]-1LL*j*d; } } } int main() { int T=read(),i,j,k; LL ans; while(T--) { n=read(); scanf("%lld",&W); W-=n; scanf("%s",s+1); get_nxt(); if(W<0) { puts("0"); continue; } for(j=0,i=nxt[n];i;i=nxt[i]) len[++j]=n-i; for(i=1;i<=(j>>1);i++) swap(len[i],len[j-i+1]); for(f[0]=0,i=1;i<n;i++) f[i]=inf; nm=n; for(i=1;i<=j;i=k) { if(i==j) { update(len[i],1,1); break; } for(k=i+1;k<=j&&len[i]-len[i+1]==len[k-1]-len[k];k++); update(len[k-1],len[i]-len[i+1],k-i); } for(ans=i=0;i<nm;i++) if(f[i]!=inf&&f[i]<=W) ans+=(W-f[i])/nm+1; printf("%lld\n",ans); } return 0; }
100分 完全不明白