T1正解KMP Next数组利用,n^2大暴力水过
T2 dp??乱搞?? 只水了50
T3 组合数? 有点难,但不是很难。 现场0分,部分分都没拿。
看题解或代码的看目录,有索引。
T1
问题 A: 重复字符串
时间限制: 1 Sec 内存限制: 256 MB
题目描述
给定两个字符串a和b,我们可以定义一些操作:a*b为将字符串a和字符串b连接起来,比如a= “aoe”,b= “jkw”,那么a*b= “aoejkw”。进一步,我们可以有指数操作,a^0= “”, a^1=a, a^2=a*a, a^n=a*(a^(n-1))=a*a*…*a (n个a)
现在给你一个字符串,你可以将它看成是a^n的形式,比如字符串”abababab”,可以认为是”abab”^2, 也可以是”abababab”^1,还可以是”ab”^4。
现在问题是,给定的字符串,我们想让它变成a^n中的n达到最大,那么这个n最大是多少?例如:”abababab”最大的n是4。
输入
第一行,一个整数m,表示有m个字符串。
接下来m行每行输入一个只含小写字母的字符串。
输出
输出m行,对于每行输出相应字符串的最大n。
样例输入
3
abcde
aaaaaa
abababab
样例输出
1
6
4
提示
30%的数据:字符串的长度≤1000;
100%的数据:字符串的长度≤1000000, m≤10,字符串内只含小写字母。
Solution
- 没错,数据水,只要for循环分成几份,O(N)判断,优化技巧:1.因数在上,2.找到了就break,万能的break!
- 100分做法:KMP,Next数组
假设字符串为S,长度为N,子串T重复K次后得到串S,那么T的长度一定为L = N/K(要整除),则T = S[1…L],将S拆分成K份,每份长度为L,则有
S[1…L] = S[L+1…2L] = S[2L+1…3L] = … = S[(K-1)L+1…KL]
由于要保证K最大,势必L要取最小,所以根据Next函数的定义,有Next[KL] = (K-1)L;
即Next[N] = N - L,所以L = N - Next[N];
但是得出的长度L还要保证能被N整除,所以如果不能整除说明L = N,即K = 1;而如果能整除,那么K = N / (N - Next[N]);
因而我们只要对字符串S做一趟KMP,对其求Next数组,剩下的就是上述结论
时间复杂度O(n)
Code
我的
#include<bits/stdc++.h>
using namespace std;
const int MAXL=1000;
char str[MAXL],a[MAXL];
int n,ans;
bool flag;
bool pd(int len)
{
for (int i=1;i<=n;i++)
{
if (i<=len) a[i%len]=str[i];
else if (str[i]!=a[i%len]) return false;
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
flag=true;
scanf("%s",str+1);
n=strlen(str+1);
for (int i=n;i>=1&&flag;i--)
if (n%i==0 && pd(n/i)) ans=i,flag=false;
printf("%d\n",ans);
}
return 0;
}
标程
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char a[1000005];
int next[1000005],len;
void getnext(){
int i=0,j=-1;
next[0]=-1;
while(i<len){
if(j==-1 || a[i]==a[j]){
i++,j++;
next[i]=j;
}else j=next[j];
}
}
int main(){
freopen("powerstr.in","r",stdin);
freopen("powerstr.out","w",stdout);
int n;
scanf("%d",&n);
while(n--){
scanf("%s",a);
//if(a[0]=='.') break;
len=strlen(a);
getnext();
int pos=next[len];
if(len%(len-pos)!=0) printf("1\n");
else printf("%d\n", len/(len-pos));
}
return 0;
}
T2
问题 B: Fibonacci进制
时间限制: 1 Sec 内存限制: 256 MB
题目描述
定义一种Fibonacci进制,可以将十进制数用Fibonacci数表示。Fibonacci进制中,每个位上的数值只有0或1,权值是Fibonacci数。令f0=f1=1,fi=fi-1+fi-2, N=an*fn+an-1*fn-1+…+a1*f1,写成N=anan-1..a2a1f。Fibonacci表示中,不能出现相邻的两个1。例如:自然数(十进制)表示为Fibonacci进制为1=1F,2=10F,3=100F,4=3+1=101F,5=1000F,6=5+1=1001F,7=5+2=1010F。
现在,Bsny将所有自然数按照Fibonacci进制,依次输出在屏幕上,110100101100010011010……现在,Bsny想知道这个长串的前N个数字中,包含多少个1。
输入
第一行一个整数N,表示统计范围是自然数的Fibonacci
表示的前N个数字
输出
一个数,前N个数字中1的个数。
样例输入
21
样例输出
10
提示
【样例解释】
前21个数字为110100101100010011010,共有10个1。
【数据规模】
30%的数据N≤1000;
50%的数据N≤106;
100%的数据N≤1015。
Solution
100分做法:DP
N很大,先尝试几个小数据。可以发现,每个Fibonacci表示的长度,和Fibonacci数大小有关(1,2,3,5,8,13,21……),这些值最高位上是1,后面全是0,即第一个Fibonacci表示长度为i的数是fib[i]。因此按照长度对Fibonacci表示进行分类,长度为i的数有fib[i-1]个,看做是第i组。那么第i组的总长度len[i] = fib[i-1]*i。
接下来,看1出现的次数。长度为i的数的表示中,第i-1位肯定是0。
Sum[i]表示前i组的1的个数。可以得到如下式子:Sum[i]=sum[i-1]+fib[i-1]+sum[i-2]。第i组首位1的个数为fib[i-1],i-1位为0,那么最后的i-2位的情况,恰好为1~i-2组的所有情况。
整体算法也就明了了:
1) 求出N位所在的Fibonacci表示的数的长度t
2) 求1~t中Fibonacci表示中1出现的个数。
3) 继续求解剩余字符的1。
例如求解得到最后对应Fibonacci表示为x=100100
1) 对于长度为1~5的Fibonacci表示中1的个数为sum[5],i<=100000中1的个数即为sum[5]+1。
2) 对于100000
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll MAXN=100;
ll fib[MAXN],slen[MAXN],cnt[MAXN],sum[MAXN],a[MAXN];
ll n,m,ans;
void prepare()
{
fib[1]=fib[2]=1;
slen[1]=1; slen[2]=3;
cnt[1]=cnt[2]=1;
sum[1]=1; sum[2]=2;
for (ll i=3;fib[i-1]<n;i++)
{
fib[i]=fib[i-1]+fib[i-2];
slen[i]=slen[i-1]+fib[i]*i;
cnt[i]=fib[i]+sum[i-2];
sum[i]=sum[i-1]+cnt[i];
m=i-1;
}
}
void change(ll x,ll &len)//change x to fib and return the length;
{
memset(a,0,sizeof(a));
len=0;
for (ll i=m;i>=1;i--)
if (x>=fib[i+1])
{
a[i]=1;
x-=fib[i+1];
if (len==0) len=i;
}
}
ll calc(ll len)
{
ll num=0,t=0;
for (ll i=1;i<=len-1;i++)
{
num+=a[i];
if (a[i]==1) t=i;
}
if (num==0)
{
ans+=sum[len-1]+1;
return fib[len+1];
}
else
{
ll x=calc(t);
ans+=sum[len-1]+1+x;
return fib[len+1]+x;
}
}
void solve()
{
ll t,x,y,len;
//sgma_i=1-n_fib[i]=fib[n+2]-1;
for (t=0;slen[t]<=n;t++);
x=n-(slen[t-1]);
y=x%t; x=(x/t)+fib[t+1]-1;
change(x,len);
ans=0;
x=calc(len);
change(x+1,len);
for (ll i=len;i>=len-y+1;i--) ans+=a[i];
printf("%lld",ans);
}
int main()
{
scanf("%lld",&n);
if (n==0) {cout<<0<<endl; return 0;}
prepare();
solve();
return 0;
}
T3
问题 C: 发奖金
时间限制: 1 Sec 内存限制: 256 MB
题目描述
Bsny最近公司运作不佳,本年度利润才m元,但员工的奖金还是要发的,公司有n个员工,怎么发奖金这个完全由老板Bsny自己决定。Bsny想要么把这m元全发了,激励一下员工,但具体怎么分配方案有很多。比如m=1, n=2, 那么可以员工1发1元,员工2发0元;也可以员工1发0元,员工2发1元,有两种方案。
但其实,Bsny还是有点吝啬的,他想这m元不一定全部作为奖金,可以部分留给自己,这样的话,发奖金的方案数就更多了。还是以m=1, n=2为例子:
方案1:员工1发1元,员工2发0元
方案2:员工1发0元,员工2发1元
方案3:员工1发0元,员工2发0元
意味着老板Bsny发的奖金范围为[0, m]。
好奇的Bsny想知道,给定n和m,他有多少种发奖金的方案?这个答案很大,所以再给定一个p,最终的答案取模p的余数.
输入
第一行三个整数n, m, p。
输出
仅一行,一个整数表示最终的答案取模p的余数。
样例输入
2 1 5
样例输出
3
提示
对于p:设p=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
20%的数据:1 ≤ n, m≤ 15;
40%的数据:1≤n, m≤1000,p=10007;
60%的数据:保证t=1,ci=1,pi^ci≤10^5;
80%的数据:t≤2,ci=1,pi≤10^5;
100%的数据:1≤ n, m≤10^9,1≤pi^ci≤10^5,所有P不超过2^31-1。
Solution
7.5考的今天才订正完……
求组合数模一个合数,上题解
100分做法:组合+质因数分解+逆元+中国剩余定理
题目相当于求n个数的和不超过m的方案数。
首先如果是恰好等于m,那么就等价于求方程x1 + x2 + … + xn = m的解的个数,利用插板法可得到公式:C(n + m - 1, m)
现在要求不大于m的,相当于对i = 0 … m对C(n + i - 1, i)求和,根据pascal递推式可以得到答案为C(n + m, m)
现在就需要求C(n + m, m) mod P
这里我们主要要解决如何快速计算n! mod P
以及当分母有m! mod P的情况
当n,m都比较小的时候,同时P为比较大的素数时,可以直接利用逆元求解,当n,m比较大的时候,见下面两种情况(以下参考魏铭2011年国家集训队作业)
当P为素数的情况:
我们发现n! mod P的计算过程是以P为周期的,举例如下:
n = 10, P = 3
n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
= 1 * 2 *
4 * 5 *
7 * 8 *
10 *
3 * 6 * 9
= (1 * 2)3 *
33 * (1 * 2 * 3)
最后一步中的1 * 2 *3可递归处理。
因为P的倍数与P不互质,所以P的倍数不能直接乘入答案,应当用一个计数器变量cnt来保存答案中因子P的个数。
我们提前预处理出fac[i] = 1 * 2 * 3 * … * (i – 1) * i mod P,函数calcfac(n)返回n! mod P的值,power(a, b, c)返回ab mod c的值,可用快速幂在O(logb)的时间内完成。
typedef long long LL;
LL calcfac(LL n)
{
if (n
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=100005;
ll fac[MAXN],inv[MAXN],A[MAXN],M[MAXN];
ll p[MAXN],c[MAXN];
ll n,m,cntp,MOD;
ll power(ll a,ll b,ll p)
{
ll ret=1;
while (b)
{
if (b&1)
{
ret*=a;
if (p!=-1) ret%=p;
}
a*=a;
if (p!=-1) a%=p;
b>>=1;
}
return ret;
}
ll extend_gcd(ll a,ll b,ll &x,ll &y)
{
if (b==0) {x=1; y=0; return a;}
ll d=extend_gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
ll getinv(ll a,ll n)
{
ll d,x,y;
d=extend_gcd(a,n,x,y);
if (d==1) return (x%n+n)%n;
else return -1;
}
ll calcfac(ll n,ll &cnt,const ll &P,const ll &p)
{
if (n<p) return fac[n];
ll seg=n/P,rem=n%P;
ll ret=power(fac[P-1],seg,P);
ret=ret*fac[rem]%P;
cnt+=n/p;
return ret*calcfac(n/p,cnt,P,p)%P;
}
ll calcinv(ll n,ll &cnt,const ll &P,const ll &p)
{
if (n<p) return inv[n];
ll seg=n/P,rem=n%P;
ll ret=power(inv[P-1],seg,P);
ret=ret*inv[rem]%P;
cnt-=n/p;
return ret*calcinv(n/p,cnt,P,p)%P;
}
void GetC(ll n,ll m)
{
for (int i=1;i<=cntp;i++)
{
ll P=power(p[i],c[i],-1);
fac[0]=1;
for(int j=1;j<P;j++)
{
fac[j]=fac[j-1];
if (j%p[i]!=0)
fac[j]=fac[j]*j%P;
}
inv[0]=1;
for (int j=1;j<P;j++)
{
inv[j]=inv[j-1];
if (j%p[i]!=0)
inv[j]=inv[j]*getinv(j,P)%P;
}
ll cnt=0;
ll ret=calcfac(n,cnt,P,p[i]);
ret=ret*calcinv(m,cnt,P,p[i])%P;
ret=ret*calcinv(n-m,cnt,P,p[i])%P;
ret=ret*power(p[i],cnt,P)%P;
A[i]=ret; M[i]=P;
}
}
ll CRT(ll a[],ll m[],ll n)
{
ll M=1,ans=0,x,y,Mi,d;
for (int i=1;i<=n;i++)
M*=m[i];
for (int i=1;i<=n;i++)
{
Mi=M/m[i];
d=extend_gcd(Mi,m[i],x,y);
ans=(ans+Mi*x*a[i])%M;
}
if (ans<0) ans+=M;
return ans;
}
int main()
{
cin>>n>>m>>MOD;
for (int i=2;i<=100000;i++)
{
if (MOD%i==0) p[++cntp]=i;
while (MOD%i==0) c[cntp]++,MOD/=i;
}
GetC(n+m,n);
cout<<CRT(A,M,cntp)<<endl;
return 0;
}