2011DLUT现场赛 2道铜牌难度题

 E

TLE的方法:【也是现场赛卡住陆神的方法,状态n^2 决策n 结果悲剧的TLE了,semilive的时候我们也用的这个方法,MLE了。。。】

当时犯了致命的错误,以为优化了转移代价就可以降低复杂度,结果发现决策也是n的。

对于一个含 I 、 D 、 ?的链,遇到?号就断链 ,然后是对于无?的序列, 可以考虑成是某个前一个序列插入一个最大值,这个最大值只能出现最在极大值,和边界上。。

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>

typedef long long ll;
using namespace std;
const int maxn=1020;
const ll mod=1000000007ll;
char str[maxn];
ll com[maxn][maxn];
ll DP[maxn*maxn];
int n;

int qmark[maxn];//以i为起点向后遇到的第一个问号的位置
int IDmark[maxn];//以i为起点向后遇到的第一个ID序列的I的位置

void init()
{
    com[0][0]=1;
    com[1][0]=1;
    com[1][1]=1;
    for (int i=2 ; i<maxn-1 ; ++i)
    {
        com[i][0]=1;
        for (int j=1 ; j<=i; ++j)
            com[i][j]=(com[i-1][j]+com[i-1][j-1])%mod;
            //,printf(" i j %d %d %d\n" , i , j , com[i][j]);
    }
}

ll dp(int sta)
{
    if(sta<0)return 1ll;
    ll ans = 0;
    int x=sta%n , y=sta/n;
    if(y>=n)return 1ll;
    if(x>y)return 1ll;
    int len=y-x+1;
    //printf("sta=%d  x=%d y=%d len=%d\n" ,sta, x , y , len);
    if(DP[sta])return DP[sta];
    if(len==1 && str[x]!='?')return DP[sta]=1ll;
    if(len==1 && str[x]=='?')return DP[sta]=2ll;
    /*
    for (int i=0 ; i<len ; ++i)
    {
        if(str[i]=='?')
        {
            //printf("%I64d\n",dp(ss.substr(0,i)));
           ans += (dp(0,i)*dp(ss.substr(i+1,len))%mod*com[len+1][i+1]%mod);
           return DP[strmap[ss]]=(ans == 0? 1:ans);
        }
    }
    */
    int &j=qmark[x];
    if(~j && j<=y)
    {
        //printf(" %d  %d ??? %d %d\n",x,y,j,j-x+1);
        ans=(dp((j-1)*n+x)*dp(y*n+j+1)%mod*com[len+1][j-x+1]%mod);
        return DP[sta]=ans;
    }
    int p=IDmark[x];
    while ((~p) && p<y)
    {
        //printf("p===%d\n",p);
        //printf("%d==\n",(p-1)*n+x);
        //printf("%d  %d==\n",len,p-x+1);
        //printf("com==%lld\n",com[len][p-x+1]);
        ans=(ans+(dp((p-1)*n+x)*dp(y*n+p+2)%mod*com[len][p-x+1]%mod))%mod;
        p=IDmark[p+1];
    }
    if(str[x]=='D')ans=(ans+dp(y*n+x+1))%mod;
    if(str[y]=='I')ans=(ans+dp((y-1)*n+x))%mod;
    return DP[sta]=(ans==0?1:ans)%mod;

    /*
    for (int i=0 ; i<len-1 ; ++i)
    {
        if(ss[i]=='I' && ss[i+1]=='D')
        {
            ans += (dp(ss.substr(0,i))*dp(ss.substr(i+2,len))%mod*com[len][i+1]%mod);
        }
    }
    if(ss[len-1]=='I')ans+=dp(ss.substr(0,len-1));
    return DP[strmap[ss]]=(ans == 0? 1:ans);
    */
}

int main()
{
    init();
    while (~scanf("%s",str))
    {
        memset (DP , 0 , sizeof(DP));
        memset (qmark , -1 , sizeof(qmark));
        memset (IDmark , -1 , sizeof(IDmark));
        n=strlen(str);
        for (int i=0 ; i<n ; ++i)
        {
            if(str[i]=='?')
            {
                int q=i;
                while (qmark[q]==-1 && q>=0)
                {
                    qmark[q]=i;
                    q--;
                }
            }
            if(i<n-1)
            {
                if(str[i]=='I' && str[i+1]=='D')
                {
                    int q=i;
                    while (IDmark[q]==-1 && q>=0)
                    {
                        IDmark[q]=i;
                        q--;
                    }
                }
            }
        }
        //for (int i=0 ; i<n ; ++i)
        //printf("qmark==%d   IDmark==%d\n", qmark[i], IDmark[i]);
        printf("%lld\n",dp((n-1)*n+0)%mod);//rear * n+head ;
    }
    return 0;
}


 

AC的方法:状态n^2 , 时间o(n^2) ,空间o(n)。

DP[i][j]表示剩余i个数 , 有j个数比当前数小

#include <cstdio>
#include <cstring>

const int maxn=1005;
const int mod=1000000007;
char str[maxn];
int dp[2][maxn];//剩下有j个比i位上的
int sum[2][maxn];
int main ()
{
    while (~scanf("%s",str))
    {
        int n=strlen(str);
        for (int i=0 ; i<=n ; ++i)dp[0][i]=1,sum[0][i]=i+1;
        int turn=1;
        for (int i=n-1 ; i>=0 ; --i , turn^=1)
        {
            memset (dp[turn] , 0 , sizeof(dp[turn]));
            for (int j=0 ; j<=i ; ++j)
            {
                if(str[n-i-1]!='D')
                {
                    //for (int k=j+1 ; k<=i+1 ; ++k)
                        //dp[i][j]+=dp[i+1][k];
                    dp[turn][j]=(dp[turn][j]+sum[turn^1][i+1]-sum[turn^1][j]+mod)%mod;
                }
                if(str[n-1-i]!='I')
                {
                    //for (int k=0 ; k<=j ; ++k)
                        //dp[i][j]+=dp[i+1][k];
                    dp[turn][j]=(dp[turn][j]+sum[turn^1][j])%mod;
                }
                sum[turn][j]=(j>0?sum[turn][j-1]+dp[turn][j]:dp[turn][j])%mod;
            }
        }
        /*
        for (int i=0 ; i<=n+1 ; ++i)
        {
            for (int j=0 ; j<=n+1 ; ++j)
                printf("sum[%d][%d]==%lld  " , i , j , sum[i][j]);
            printf("\n");
            for (int j=0 ; j<=n+1 ; ++j)
                printf("dp[%d][%d]==%lld  ", i , j ,dp[i][j]);
            printf("\n");
        }
        */
        printf("%d\n",dp[turn^1][0]%mod);
    }
    return 0;
}


 

I题, 老朱用JAVA大数1Y了,我又用C的逆元敲了下

C代码:

#include <cstdio>
#include <cstring>
#include <cmath>

typedef long long typen;
typen mod=1000000007ll;
///
typen x , y;
typen eGCD(typen a , typen b )
{
    if(!b)
    {
        x=1 , y=0;
        return a;
    }
    int d=eGCD(b , a%b);
    int t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
///
const int maxP=10005;
int prime[maxP+1];
void getprime()
{
    memset (prime , 0 , sizeof(prime));
    for (int i=2 ; i<=maxP ; ++i)
    {
        if (!prime[i])prime[++prime[0]]=i;
        for (int j=1 ; j<=prime[0] && prime[j]*i<=maxP ; ++j)
        {
            prime[prime[j]*i]=1;
            if(i%prime[j]==0)break;
        }
    }
}

int factor[100][2];
int faccnt;
void getFactors(int x)
{
    faccnt=0;
    int  tmp=x;
    for (int i=1 ; prime[i]*prime[i]<=tmp ; ++i)
    {
        factor[faccnt][1]=0;
        if(tmp%prime[i]==0)
        {
            factor[faccnt][0]=prime[i];
            while (tmp%prime[i]==0)
                factor[faccnt][1]++,tmp/=prime[i];
            ++faccnt;
        }
    }
    if(tmp != 1)
        factor[faccnt][0]=tmp , factor[faccnt++][1]=1;
}
///
int depth;

typen inverse2 , inverse3 , inverse5 ;
typen get_n4(typen x)
{
    x=(x*x)%mod;
    return (typen)(x*x)%mod;
}

typen get_sigma_n4(typen n)
{
    return ( inverse5*get_n4(n+1)%mod*(n+1)%mod
            -inverse5-inverse5*n%mod+mod+mod+mod
            -inverse2*(n*n%mod*(n+1)%mod*(n+1)%mod)%mod
            -inverse2*n%mod*(n+1)%mod+mod+mod+mod+mod
            -inverse3*n%mod*(n+1)%mod*(n<<1|1)%mod)%mod;
}
int n;
typen ans;

void dfs(int p , int cnt , int mul)//Exclusion Principle
{
    if(p==depth)
    {
        if(cnt&1)ans=(ans-get_n4(mul)*get_sigma_n4(n/mul)%mod+mod)%mod;
        else ans=(ans+get_n4(mul)*get_sigma_n4(n/mul)%mod+mod)%mod;
    }
    /*
    printf("%d  %d \n" , depth , cnt);
    printf("~!~! n4===%lld  sigma==%lld %d\n" , get_n4(mul) , get_sigma_n4(n/mul) , n/mul);
    printf("ans==%lld  mul==%d p==%d", ans , mul , p);
    printf("  f[%d]=%d" , p,factor[p][0]);
    printf("dfs(%d %d %d)\n",p , cnt ,mul );
    */
    else
    {
        dfs(p+1 , cnt+1 , mul*factor[p][0]);
        dfs(p+1 , cnt , mul);
    }
}

int main ()
{
    int cas;
    scanf("%d",&cas);
    getprime();
    eGCD(2 , mod);
    inverse2=(x+mod)%mod;
    eGCD(3 , mod);
    inverse3=x;
    eGCD(5 , mod);
    inverse5=(x+mod)%mod;
    while (cas--)
    {
        scanf("%d",&n);
        getFactors(n);
        depth=faccnt;
        //printf("%lld\n",get_sigma_n4(n));
        //printf("%lld\n",get_n4(n));
        ans=0;
        dfs(0 , 0 , 1);
        printf("%d\n",ans%mod);
    }
    return 0;
}


JAVA代码,以后学了JAVA 这个拿来当模板了:

import java.util.Scanner;
import java.math.BigInteger;
public class Main {

    /**
     * @param args the command line arguments
     */
    static int prime[] = new int[10010];
    static int getPrime()
    {
    for(int i = 0; i < 10010; ++ i) prime[i] = 0;
    for (int i = 2; i <= 10000; i++)
    {
            if (prime[i] == 0) prime[++prime[0]] = i;
            for (int j = 1; j <= prime[0] && prime[j]*i<= 10000; j++)
            {
                prime[prime[j]*i] = 1;
                if (i % prime[j] == 0) break;
            }
        }
        return prime[0];
    }
static int factor[][] = new int[100][3];
static int facCnt;
static int getFactors(int x)
{
    facCnt = 0;
    int tmp = x;
    for(int i = 1; prime[i] <= tmp / prime[i]; i++)
    {
        factor[facCnt][1] = 1;
        factor[facCnt][2] = 0;
        if(tmp % prime[i] == 0)
            factor[facCnt][0] = prime[i];
        while(tmp % prime[i] == 0)
        {
            factor[facCnt][2]++;
            factor[facCnt][1] *= prime[i];
            tmp /= prime[i];
        }
        if(factor[facCnt][1] > 1) facCnt++;
    }
    if(tmp != 1)
    {
        factor[facCnt][0] = tmp;
        factor[facCnt][1] = tmp;
        factor[facCnt++][2] = 1;
    }
    return facCnt;
}
static BigInteger sumpower(int n){
    BigInteger ret = BigInteger.valueOf(n);
    ret = (ret.multiply(ret.add(BigInteger.ONE)).
            multiply(ret.multiply(BigInteger.valueOf(2)).add(BigInteger.ONE)).
            multiply(BigInteger.valueOf(3).multiply(ret.multiply(ret)).add(BigInteger.valueOf(3).multiply(ret)).subtract(BigInteger.ONE))).
            divide(BigInteger.valueOf(30));
    return ret.mod(modulo);

}
static BigInteger ans;
static BigInteger modulo =  new BigInteger("1000000007");
static void dfs(int idx, int fact,int cnt, int n)
{
    if(idx == facCnt)
    {
        if(cnt % 2 == 1) ans = ans.subtract(BigInteger.valueOf(fact).pow(4).multiply(sumpower(n/fact))).add(modulo).mod(modulo);
        else ans = ans.add(BigInteger.valueOf(fact).pow(4).multiply(sumpower(n/fact))).mod(modulo);
    }
    else
    {
        dfs(idx+1, fact * factor[idx][0], cnt + 1, n);
        dfs(idx+1, fact , cnt, n);
    }
}
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int t, n;
        getPrime();
        t = scan.nextInt();
        while(t-- > 0)
        {
            n = scan.nextInt();
            getFactors(n);
            ans = BigInteger.ONE;
            dfs(0,1,0,n);
            System.out.println(ans.subtract(BigInteger.ONE).add(modulo).mod(modulo));
        }
    }

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值