题意:小明的上学路上有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;
}