bzoj 3609: [Heoi2014]人人尽说江南好

对于必胜方,游戏的最优策略是每次找最大的两个可以合并的数合并,但是如果出现(m-1)  2 1 1 ……的情况合并 m-1 和 2,这样无论必败方如何操作,这次游戏的总操作次数是一定的,因为这样的终局一定是一些m,然后一堆n%m,或者其他总操作次数和它相等的局面。这个终局的总操作次数相当于比没有m的限制少了这剩下的n/m上取整堆的合并次数,所以操作次数是(n-1)-(ceil(n/m)-1)
    
    
    
    
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
 
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md 10000007
using namespace std;
struct Ju
{
ll a[3][3];
int x,y;
ll *operator [] (int x) { return a[x];}
} a,ju,mid,ans;
 
Ju operator *=(Ju &a,Ju &b)
{
memset(mid.a,0,sizeof(mid.a)); mid.x=a.x; mid.y=b.y;
for (int i=0;i<a.x;i++)
for (int j=0;j<b.y;j++)
for (int k=0;k<a.y;k++)
mid[i][j]=(mid[i][j]+a[i][k]*b[k][j])%md;
a=mid;
}
 
void kpow(Ju &a,int b)
{
memset(ans.a,0,sizeof(ans.a)); ans.x=a.x; ans.y=a.y;
for (int i=0;i<a.x;i++) ans[i][i]=1;
while (b)
{
if (b&1) ans*=a;
a*=a; b>>=1;
}
a=ans;
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
int A=-inf,B=-inf,sum=0;
for (int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum=(sum+x)%md;
if (x>A) { B=A; A=x;}
else if (x>B) { B=x;}
}
while (B<=0) { k--; B+=A; sum=(sum+B)%md;}
ju[0][0]=0; ju[0][1]=1; ju[0][2]=1;
ju[1][0]=1; ju[1][1]=1; ju[1][2]=1;
ju[2][0]=0; ju[2][1]=0; ju[2][2]=1;
ju.x=ju.y=3;
kpow(ju,k);
a.x=1; a.y=3; a[0][0]=B; a[0][1]=A; a[0][2]=0;
a*=ju; printf("%lld\n",(a[0][2]+sum+md)%md);
return 0;
}


你可能感兴趣的:(bzoj 3609: [Heoi2014]人人尽说江南好)