BestCoder Round #78

A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5655

官方题解:

By NanoApe 构成四边形的条件:最长边小于其余三条边的和

坑点1:假如有条长度为0的边的话,哪里来的四条边呢?

坑点2:其余三条边的和会爆longlong,我们可以将a+b+c>d换成a>d-b-c来求解

我的理解:

没想到a+b+c>d转化成a>d-b-c,想到经常自己写程序相加爆longlong的话会变成负的,那肯定是大于第四条边了,利用了下这个条件,过了

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL __int64
int main()
{
    int T;
    LL a,b,c,d,maxn,sum,sum1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&d);
        if(a==0||b==0||c==0||d==0)
        {
            printf("No\n");
            continue;
        }
        maxn=max(max(a,b),max(c,d));
        sum=-maxn;
        sum+=a;
        sum1=sum;
        sum+=b;
        if(sum<sum1){printf("Yes\n");continue;}
        sum1=sum;
        sum+=c;
        if(sum<sum1){printf("Yes\n");continue;}
        sum1=sum;
        sum+=d;
        if(sum<sum1){printf("Yes\n");continue;}
        sum1=sum;
        if(sum>maxn)
            printf("Yes\n");
        else
            printf("No\n");
    }
}

B题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5656

官方题解:

By YJQ 我们令dp[i][j]表示在前i个数中,选出若干个数使得它们的gcd为j的方案数,于是只需要枚举第i+1个数是否被选中来转移就可以了

令第i+1个数为v,当考虑dp[i][j]的时候,我们令dp[i+1][j] += dp[i][j]dp[i+1][j]+=dp[i][j](v 不选),dp[i+1][gcd(j,v)] += dp[i][j]dp[i+1][gcd(j,v)]+=dp[i][j](v 选)

复杂度O(N*MaxV) MaxV 为出现过的数的最大值

其实有O(MaxV *log(MaxV))的做法,我们考虑记f[i]表示从这些数中选择若干个数,使得他们的gcd是i的倍数的方案数。假如有K个数是i的倍数,则f[i]=2^K-1,再用g[i]表示从这些数中选择若干个数,使得他们的gcd是i的方案数,则g[i]=f[i] - g[j] (对于所有j是i的倍数)。

由调和级数可以得到复杂度为O(MaxV *log(MaxV))

我的理解:

当时做的时候,就钻进爆搜的眼里面去了,一直在想怎么预处理加快速度,就跟掉钱眼了一样,拔不出来,o(╯□╰)o,比赛的时候也一样,希望下次不要死钻一个方向,多方向解题。

dp[i]记录gcd为i的方案数

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005;
const int mod=1e8+7;
#define ll __int64
int a[N],g[N][N];
ll dp[N];
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int i,j,T,n;
    for(i=1;i<=1000;i++)
    {
        for(j=1;j<=1000;j++)
            g[i][j]=g[j][i]=gcd(i,j);
    }
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
        {
            for(j=1;j<=1000;j++)
            {
                if(!dp[j])continue;
                int t=g[a[i]][j];
                dp[t]=dp[t]+dp[j];
                if(dp[t]>mod) dp[t]%=mod;
            }
            dp[a[i]]++;
        }
        ll ans=0;
        for(i=1;i<=1000;i++)
        {
            if(!dp[i])continue;
            (ans+=(i*dp[i]%mod))%=mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

D题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5658

官方题解:

By NanoApe 由于字符串长度最多为1000,不同的询问最多也只有500500种,那么我们先把所有询问的答案预处理出来即可。

从小到大枚举左端点,再从小到大移动右端点,用回文树维护本质不同的回文子串。

设我们现在枚举的区间是[l,r],下一步是[l,r+1],那么就相当于在回文树所维护的字符串的后面添加一个字符。当左端点改变时,重建回文树。

复杂度O(n^2)

其实还可以用manacher,在扩展的时候用Hash去重,复杂度O(n^2)


我的题解:

回文树= = 以后再看吧

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1100;
int n,q;
char s[maxn];
int l,r;
ll ans[maxn][maxn];
struct PalinTree
{
    int ch[maxn][26],f[maxn];
    int n,tot,last;
    int len[maxn],cnt[maxn];
    int s[maxn];
    int newnode(int l)
    {
        memset(ch[tot],0,sizeof(ch[tot]));
        cnt[tot]=0;
        len[tot]=l;
        return tot++;
    }
    void init()
    {
        tot=0;
        newnode(0);
        newnode(-1);
        last=0;n=0;
        s[n]=-1;f[0]=1;
    }
    int get_fail(int x)
    {
        while(s[n-len[x]-1]!=s[n])
            x=f[x];
        return x;
    }
    void add(int c)
    {
        c-='a';
        s[++n]=c;
        last=get_fail(last);
        if(!ch[last][c])
        {
            int cur=newnode(len[last]+2);
            f[cur]=ch[get_fail(f[last])][c];
            ch[last][c]=cur;
        }
        last=ch[last][c];
        cnt[last]++;
    }
}pt;
void init()
{
    int len=strlen(s+1);
    for(int l=1;l<=len;l++)
    {
        pt.init();
        for(int r=l;r<=len;r++)
        {
            pt.add(s[r]);
            ans[l][r]=pt.tot-2;
        }
    }
}
int main()
{
    while(~scanf("%d",&n))
    {
        scanf("%s",s+1);
        init();
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d%d",&l,&r);
            printf("%I64d\n",ans[l][r]);
        }
    }
    return 0;
}



你可能感兴趣的:(BestCoder Round #78)