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;
}