Codeforces Round #307 (Div. 2)--C. GukiZ hates Boxes、D. GukiZ and Binary Operations

                                                        C. GukiZ hates Boxes

题意:小明的上学路上有m堆石头,每堆石头有a[i]个石头,现在有m个同学帮助小明移开这些石头,问最少需要多少时间把所有石头移开。每一秒,每个人可以选择向前走一步,也可以将当前堆的石头移走一个。

二分总时间,贪心选择。已知总共有m个人,那么每个人的总时间都已经知道了,我们从后往前或者从前往后贪心都可以,先用尽一个人的时间,不够再补,只要出现所有人的时间都用完了还不够的情况下返回false。

这是2017陕西省赛全国邀请赛热身赛的第二题,当时想复杂了, 想着直接用总时间去贪心,然后赛场上贪心写错了二分也没时间写出来,后来补题从前往后贪心又莫名奇妙wa了,今天再写结果两种贪心都1A了。

注意数据范围:二分上界至少1e15。

贪心思路一:

const int N=1e5+10;
int n,m;
ll a[N],b[N];
bool judge(ll k)
{
    for(int i=1;i<=n;i++) b[i]=a[i];
    int num=m;
    ll rest=0;
    for(int i=1;i<=n;i++)
    {
        while(b[i])
        {
          if(rest<=0) rest+=k-i,num--;
          ll tmp=min(rest,b[i]);
          b[i]-=tmp;
          rest-=tmp;
          if(num<0||rest<0) return false;
        }
        if(rest) rest--;//剩余的时间,往前走
    }
    return true;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
        ll l=0,r=1e15,ans=0;
        while(l<=r)
        {
            ll mid=(l+r)>>1;
            if(judge(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


贪心思路二:

const int N=1e5+10;
int n,m;
ll a[N],b[N];
bool judge(ll k)
{
    for(int i=1;i<=n;i++) b[i]=a[i];
    int num=m;
    ll rest=0;
    for(int i=n;i>=1;i--)
    {
        while(b[i])
        {
          if(rest<=0) rest+=k-i,num--;
          ll tmp=min(rest,b[i]);
          b[i]-=tmp;
          rest-=tmp;
        if(num<0||rest<0) return false;
//          if(num<0) return false;
        }
    }
    return true;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
        ll l=0,r=1e15,ans=0;
        while(l<=r)
        {
            ll mid=(l+r)>>1;
            if(judge(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


                                          D. GukiZ and Binary Operations 

 很有意思的一个题,组合数学是我们的弱项,所以得克服一下。

题意:给你n,k,l,m。数据范围都很大,求有多个序列满足:(a1&a2)|(a2&a3)|...|(an-1&an)=k。

一眼看没什么思路,仔细分析,找到了突破口。

注意:(a1&a2)|(a2&a3)=a2&(a1|a3),

同理:(a2&a3)|(a3&a3)=a3&(a2|a4).

原式即:a2&(a1|a3)| a3&(a2|a4)...

注意:如果a1和a2或者a2和a3或ai和ai+1的某一位同时为1,那么最后这一位得出的结果必然是1。也就是如果k的某一位为1,那么必然有ai和ai+1的那一位同时为1,否则最后得出的那一位为0.

我们只需算出n位,相邻两位同时为1有多少种即可。

打个表发现不同时为1的方案满足斐波那契数列:

const int N=1e5+10;
int dis_adc;
ll n,k;
int l,m;
void dfs(int now,int n,int num)
{
    if(num==n)
    {
        dis_adc++;
        return ;
    }
    if(now==0) dfs(1,n,num+1);
    dfs(0,n,num+1);
}
void discuss()
{
    for(int i=2; i<=10; i++)
    {
        dis_adc=0;
        dfs(1,i,1);
        dfs(0,i,1);
        printf("i=%2d  tot=%-4d   dis_adj=%d  adj=%d\n",i,1<<i,dis_adc,(1<<i)-dis_adc);
    }
}
struct martix
{
    ll a[2][2];
};
martix multi(martix A,martix B)
{
    martix res;
    memset(res.a,0,sizeof(res.a));
    for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
            for(int k=0; k<2; k++)
                res.a[i][j]=(res.a[i][j]+A.a[i][k]*B.a[k][j])%m;
    return res;
}
martix ksm(ll n,martix res)
{
    martix ans;
    memset(ans.a,0,sizeof(ans.a));
    ans.a[0][0]=ans.a[1][1]=1;
    while(n)
    {
        if(n&1) ans=multi(ans,res);
        res=multi(res,res);
        n>>=1;
    }
    return ans;
}
ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%m;
        a=a*a%m;
        b>>=1;
    }
    return ans;
}
ll get()
{
    martix res,ans;
    memset(ans.a,0,sizeof(ans.a));
    memset(res.a,0,sizeof(res.a));
    res.a[0][0]=res.a[1][0]=res.a[0][1]=1;
    ans.a[0][0]=5,ans.a[0][1]=3;
    if(n==2) return 3;
    else if(n==3) return 5;
    res=ksm(n-3,res);
    ans=multi(ans,res);
    return ans.a[0][0];
}
void solve()
{
    ll x=k;
    int num1=0,num0=0;//二进制位0和1的个数,加起来为总长度
    while(x)
    {
        if(x&1) num1++;
        else num0++;
        x/=2;
    }
//    printf("%d %d\n",num0,num1);
    if(num1+num0>=l+1)  puts("0");
    else
    {
      if(l-num0-num1>0) num0+=l-num1-num0;
       ll disadjacent,adjacent,ans=0;
       disadjacent=get(),adjacent=(ksm(2,n)-disadjacent+m)%m;
//       printf("%lld %lld\n",disadjacent,adjacent);
       ans=(ksm(disadjacent,num0)*ksm(adjacent,num1))%m;
       printf("%lld\n",ans);
    }
}
int main()
{
//   discuss();
    cin>>n>>k>>l>>m;
    solve();
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值