【NOIP2017SummerTraining0705】

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

  1. 沒錯,數據水,只要for循環分成幾份,O(N)判斷,優化技巧:1.因數在上,2.找到了就break,萬能的break!
  2. 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的情況

  1. 當n,m都比較小的時候,同時P爲比較大的素數時,可以直接利用逆元求解,當n,m比較大的時候,見下面兩種情況(以下參考魏銘2011年國家集訓隊作業)

  2. 當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;
}
發佈了78 篇原創文章 · 獲贊 17 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章