BestCoder Round #79

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

官方题解:

不妨令n\leq mnm

如果n>6n>6,由于所有角都大于120度且小于180度,也就是说,两个角一定不够,而三个角一定过多。因此一定无解;

n\leq6n6时,如果n=3n=3n=4n=4n=6n=6,那么显然只需要正nn边形的角就可以了。如果n=5n=5,则已经有一个108度的角。若这种角:不取,则显然仅当m=6m=6时有解;取1个,则还差360-108=252360108=252(度),但是没有一个正mm边形的内角的度数是252的约数;取2个,则还差360-108\times2=144360108×2=144(度),这恰好是正10边形的内角,取3个,则还差360-108\times3=36360108×3=36(度),也不可能满足;取大于3个也显然不可能。

因此得到结论:当nnmm中至少有一个为3或4或6时,或者当nnmm中一个等于5另一个等于10时,有解,否则无解,时间复杂度为

O\left(T\right)O(T)

我的思考:

大于6边形肯定不行,答案为小数的肯定不行,所以角度直接取整数就可以了

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
    int T,x,y,i,j,n,m,flag;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        x=180*(n-2)/n;
        y=180*(m-2)/m;
        flag=0;
        for(i=0;i<=360/x;i++)
        {
            for(j=0;i*x+j*y<=360;j++)
            {
                if(i*x+j*y==360) flag=1;
            }
        }
        if(flag)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

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

官方题解

考虑从高位到低位贪心,对于每一位,如果x,y只有唯一的取法,那么只能这么取;否则贪心地必须使答案的这一位等于1。如果x,y都是0,1都能取,则设这是从右向左数第len位,因为x,y能取的值一定都是连续的一段,因此x,y的后len位都能取0111...1(len-1个1)和1000...0(len-1个0)(否则做不到从右向左数第len位都能取0,1)。也就是说,后len位的贡献一定能达到可能的上界111...1(len个1)。此时不必继续考虑后面的位。

如果x,y在这一位并不是0,1都能取,那么由于要使得答案的这一位等于1,也只有唯一的取法。

至此,这一位考虑完毕,然后根据选取的方案,修正一下x和y的范围,然后对后一位做即可。

我的思考

贪心 自己贪心的时候考虑的不清楚

#include<cstdio>
using namespace std;
#define ll long long
int main()
{
    int T;
    scanf("%d",&T);
    ll a,b,c,d,aa,bb,cc,dd,ans;
    while(T--)
    {
        ans=0;
        scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        for(ll cur=1ll<<61;cur>0;cur>>=1)
        {
            aa=a&cur;
            bb=b&cur;
            cc=c&cur;
            dd=d&cur;
            if(aa==bb)//如果x在这一位只能取0或1
            {
                if(cc==dd)//如果y在这一位也只能取0或1
                    ans|=aa^cc;
                else//如果y可以取0也可以取1
                {
                    ans|=cur;//贪心,当前位必可以取到1
                    if(aa==0)//如果x只能取0 那么y只能取1 那么后面y的下限就变成了00000
                        c&=cur;
                    else//如果x只能取1 那么y只能取0 那么后面y的上限就变成了11111
                        d|=cur-1;
                }
            }
            else//如果x可0可1
            {
                ans|=cur;//贪心取1
                if(cc==dd)//如果y只能取0或者1
                {
                    if(cc==0)//和上面一样
                        a&=cur;
                    else
                        b|=cur-1;
                }
                else//如果x可0可1 y可0可1 那么从这一位开始往后数肯定全部能凑成1111
                {
                    ans|=cur-1;
                    break;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

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

官方题解:

先枚举kk,将所有A_{i\times k}Ai×kii是正整数且i\times k<=ni×k<=n)取下来存到B_iBi,于是将原问题转化成了下述问题:对于给定的正整数序列

B_1,B_2,...,B_{\lfloor\frac{n}{k}\rfloor}B1,B2,...,Bkn,求出连续的一段,使得这段的和值乘以这段的最小值的结果最大。

我们可以枚举最小值,设其在第ii位出现,此时我们只要和值最大就可以了。设B_iBi向左第一个小于B_iBi的数是ll(若没有则l=0l=0),向右第一个小于BiBi的数是rr(若没有则r=\lfloor\frac{n}{k}\rfloor+1r=kn+1)。

则保证最小值不变的最大和值是B_{l+1},B_{l+2}...B_{r-1}Bl+1,Bl+2...Br1这段。可以使用单调栈这种数据结构来在O(\lfloor\frac{n}{k}\rfloor)O(kn)的复杂度下计算对于每个iillrr,枚举最小值出现位置及更新答案的复杂度也是

O(\lfloor\frac{n}{k}\rfloor)O(kn)

再考虑外层的枚举k这一部分,总复杂度为O\left(1+\frac{n} {2}+...+\frac{n}{n}\right)O(1+2n+...+nn)O(n\times(1+\frac{1}{2}+...+\frac{1}{n}))O(n×(1+21+...+n1)),由于调和级数1+\frac{1}{2}+...+\frac{1}{n}1+21+...+n1lognlogn级别,因此时间复杂度为O\left(nlogn\right)O(nlogn)

我的思考:思路同官方题解

#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
#define ll __int64
const int N=3e5+10;
int n,a[N],R[N],L[N],st[N];
ll sum[N];
vector<int> G;
ll gao()
{
    int m=G.size();
    int top=0;
    for(int i=0;i<m;i++)
    {
        L[i]=-1;
        R[i]=m;
        if(i==0)sum[i]=G[i];
        else sum[i]=sum[i-1]+G[i];
    }
    for(int i=0;i<m;i++)//单调栈求所有点的右边界
    {
        if(top==0)st[top++]=i;
        else
        {
            while(top!=0&&G[st[top-1]]>G[i])//将栈中所有大于当前要加入的出栈并更新那些值
            {
                R[st[top-1]]=i;
                top--;
            }
            st[top++]=i;//入栈
        }
    }
    top=0;
    for(int i=m-1;i>=0;--i)//同上
    {
        if(top==0)st[top++]=i;
        else
        {
            while(top!=0&&G[st[top-1]]>G[i])
            {
                L[st[top-1]]=i;
                top--;
            }
            st[top++]=i;
        }
    }
    ll ans=0,tmp;
    for(int i=0;i<m;i++)
    {
        if(L[i]==-1)tmp=0;
        else tmp=sum[L[i]];
        ans=max(ans,1ll*G[i]*(sum[R[i]-1]-tmp));
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        ll ans=0;
        for(int k=1;k<=n;k++)
        {
            G.clear();
            for(int i=1;i*k<=n;i++)G.push_back(a[i*k]);
            ans=max(ans,1ll*gao()*(int)sqrt(k+0.5));
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

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

官方题解:

不妨令n\leq mnm

对于这一题,我们可以将所求的有多少对正整数对的最大公约数不为完全平方数转化为有多少对最大公约数为完全平方数。

那么我们设函数F\left(x\right)F(x),当且仅当xx为完全平方数时函数值为1,否则函数值为0。那么

Ans=\sum_{i=1}^n\sum_{j=1}^mF\left(\gcd\left(i,j\right)\right)Ans=i=1nj=1mF(gcd(i,j)) 设d=\gcd\left(i,j\right)d=gcd(i,j),那么

Ans=\sum_{i=1}^n\sum_{j=1}^mF\left(d\right)Ans=i=1nj=1mF(d)。 然后我们推一下这个式子:

Ans=\sum_{d=1}^nF\left(d\right)\times\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\left[\gcd\left(i,j\right)=1\right]Ans=d=1nF(d)×i=1dnj=1dm[gcd(i,j)=1]

=\sum_{d=1}^nF\left(d\right)\times\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum_{t|i,t|j}\mu\left(t\right) =\sum_{d=1}^nF\left(d\right)\times\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\mu\left(t\right)\times\lfloor\frac{n}{dt}\rfloor\times\lfloor\frac{m}{dt}\rfloor=d=1nF(d)×i=1dnj=1dmti,tjμ(t)=d=1nF(d)×i=1dnμ(t)×dtn×dtm 然后我们设G=dtG=dt,则Ans=\sum_{G=1}^n\left(t\right)\times\lfloor\frac{n}{G}\rfloor\times\lfloor\frac{m}{G}\rfloor\times\sum_{t|G}\mu\left(t\right)\times F\left(\frac{G}{t}\right)Ans=G=1n(t)×Gn×Gm×tGμ(t)×F(tG) 然后我们设g(x)=\sum_{t|x}\mu\left(t\right)\times F\left(\frac{x}{t}\right)g(x)=txμ(t)×F(tx),则Ans=\sum_{G=1}^n\left(t\right)\times\lfloor\frac{n}{G}\rfloor\times\lfloor\frac{m}{G}\rfloor\times g(G)Ans=G=1n(t)×Gn×Gm×g(G)

那么这道题的解法就出来了,如果我们已经确定gg函数的前缀和,那么就只需要类似莫比乌斯反演的方法O\left(\sqrt{n}\right)O(n)算一下即可。

现在我们来看如何求gg函数,这肯定需要预处理。有这个函数的表达式我们可以进行分类讨论,可以求出g\left(x\times p\right)g(x×p)pp是质数且不为xx的约数)与g\left(x\right)g(x)的关系然后欧拉筛。

上述算法的总时间复杂度是O\left(m+T\sqrt{n}\right)O(m+Tn)

不过有另一种比较暴力的解法就是枚举nn范围内的完全平方数然后暴力求gg函数,这个时间复杂度是O(跑得过)。我测了一下10^7107范围内的运算次数也就是1.7\times10^71.7×107次左右运算,是可以应付的。

我的思考:莫比乌斯反演 还不是特别理解数论 慢慢看吧

<pre name="code" class="cpp">#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll __int64
using namespace std;
const int N=1e7+1;
int p[N/10],cnt,u[N],g[N];
bool vis[N];
void init()
{
    int i,j;
    vis[1]=u[1]=1;
    for(i=2;i<N;i++)
    {
        if(!vis[i])
        {
            p[++cnt]=i;
            u[i]=-1;
        }
        for(j=1;j<=cnt;j++)
        {
            if(i*p[j]>=N)break ;
            vis[i*p[j]]=1;
            u[i*p[j]]=-u[i];
            if(i%p[j]==0)
            {
                u[i*p[j]]=0;
                break;
            }
        }
    }
    for(i=1;i*i<N;i++)
        for(j=i*i;j<N;j+=i*i)
            g[j]+=u[j/i/i];
    for(i=2;i<N;i++)
        g[i]+=g[i-1];
}
int main()
{
    init();
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        if(n>m)swap(n,m);
        ll ret=(ll)n*m;
        for(int i=1,last;i<=n;i=last+1)
        {
            last=min(n/(n/i),m/(m/i));
            ret-=(ll)(g[last]-g[i-1])*(m/i)*(n/i);
        }
        printf("%I64d\n",ret);
    }
    return 0;
}


 
 

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