2012-0017

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

占by ZhouYuChen

题意是说,一个正多边形有N个点,每个点有2个属性值(1..P,1..Q),相邻两个点必须有一种属性相同。

给出8个点的属性,求样的正多边形数量。

首先要判掉一些可能的trick,比如8个点有重,给的点有直接相邻,P=1,或 Q=1,那么答案是另一个维度Q(P)的(N-(确定点个数的次方))次方。

然后可以在两端确定的情况下,计算中间不确定的点的数量。

然后根据属性值的在1..P,1..Q 任意值都是等价的,只要处理,两端属性是否有若干维相同就可以。

比如一端是(A,B),中间有L个待填。用x,y表示一个状态。x != A ,y != B.

那么另一端和中间的,可表示成 xy ,xB ,Ay,AB,这段可一根据他的状态表示成一个列矩阵M[0],比如Ay就是 [0 0 1 0]T,表示在各种状态下的数量。

那么就可以如此题目的约束写出递推阵。

M[n+1] = D * M[n]

其中D = 

[
{{{
  P+Q-3 Q-1 P-1 0

  1     P-1  0  P-1

  1     0  Q-1 Q-1

  0     1   1   1
}}}

]

比如n+1状态是AB 上一个只可能是Ay xB AB   : (AB)_(n+1) = (Ay)_(n) + (xB)_(n) + (AB)_(n),

如果是 xB 上一个是 xy xB AB x有P-1种,于是    (xB)_(n+1) = (xy)_(n)*(P-1) + (xB)_(n)*(P-1) + (AB)_(n).

检查矩阵是否正确,要算每一列,的和是不是P+Q-1。一个点与上个点可以相邻有P+Q-1中情况。

在长度为L的不缺定的情况下,第L个的状态是M[L] = D^L * M[0];

然后看端点AB,答案就是 (AB)_(L) +(Ay)_(L) + (xB)_(L) 

{{{
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef unsigned long long num;

const num M=1234567890;
num N,P,Q;
num jc[40][4][4];
num num_pow(num a,num n){
    num ret = 1;
    a%=M;
    for(;n;n>>=1){
        if(n&1)ret=ret*a%M;
        a=a*a%M;
    }
    return ret;
}
void mul(num z[][4],num x[][4],num y[][4]){
    memset(z,0,sizeof(num)*16);
    for(size_t i=0;i<4;++i)
        for(size_t j=0;j<4;++j)
            for(size_t k=0;k<4;++k)
                z[i][j]=(z[i][j]+x[i][k]*y[k][j])%M;
}
void mul_eq(num z[][4],num x[][4]){
    static num tmp[4][4]; 
    memcpy(tmp,z,sizeof(tmp));
    mul(z,tmp,x);
}

num gao(num L,bool eq1,bool eq2){
    if(L==0)
        return (eq1||eq2)?1:0;
    if(P==1||Q==1)
        return num_pow((P+Q-1)%M,L);
    static num ret[4][4];
    memset(ret,0,sizeof(ret));
    for(int i=0;i<4;++i)ret[i][i]=1;
    num t[4][4];mul(t,ret,ret);
    for(int i=0;i<40;++i)
    if(1ull<<i & L)
        mul_eq(ret,jc[i]);
    return ret[1][(eq1?2:0)+(eq2?1:0)]+ret[2][(eq1?2:0)+(eq2?1:0)]+ret[3][(eq1?2:0)+(eq2?1:0)];
}

num x[8],u[8],v[8];
num solve(){
#define mp make_pair
    for(int i=0;i<8;++i)
        scanf("%lld%lld%lld",x+i,u+i,v+i);
    map<num,pair<num,num> > ss;
    for(int i=0;i<8;++i)
        if(ss.count(x[i])){
            if(ss[x[i]]!= mp(u[i],v[i]))return 0;
        }
        else ss[x[i]]=mp(u[i],v[i]);
    int m = 0;
    num a[4][4]={
        {(P+Q-3+M)%M, (Q-1)%M,(P-1)%M,0},
        {1,(P-1)%M,0,(P-1)%M},
        {1,0,(Q-1)%M,(Q-1)%M},
        {0,1,1,1}
    };
    memcpy(jc[0],a,sizeof(a));
    for(int i =1;i<=40;++i)
        mul(jc[i],jc[i-1],jc[i-1]);
    for(__typeof(ss.begin()) it=ss.begin();it!=ss.end();++it,++m){
        x[m]=it->first;
        u[m]=it->second .first;
        v[m]=it->second .second;
    }
    num  ans = 1;
    for(size_t i=1;i<m;++i)
        ans = ans * gao(x[i]-x[i-1]-1,(u[i-1]==u[i]),(v[i-1]==v[i])) % M;
    ans =ans * gao(x[0]+N-x[m-1]-1,(u[m-1]==u[0]),(v[m-1]==v[0]))%M;
    return ans;
}
int main(){
    for(;scanf("%lld%lld%lld",&N,&P,&Q)==3;)
        printf("%lld\n",solve());
    return 0;
}
}}}



---------------------------------------------------------------我是无辜的分割线-----------------------------------------------------------------------------------------
学长好快,仰慕学长。

题意学长已经说了,这里便不作多余阐述。

题目中一个关键条件便是相邻两点之间,两种属性至少有一个相同。利用这个条件,我们可以把问题变成,在一个K*S的棋盘上,从某点出发,每次走到同行同列的任意一个点上。求从一个点出发走N步后回到原点的方案数,当中8步一定要走到某些给定的点(有冲突的可以预处理掉,当然算法够健壮也可以直接处理掉)。不难发现(也可以人肉做几组观察)到棋盘上的点可以分成四种:起点,与起点同行不同列,与起点同列不同行,与起点不同列不同行。记走N步后走到点(x,y)的方案数为f(N,x,y),不难发现,若(x1,y1)与(x2,y2)是同类点,则 f(N,x1,y1) == f(N,x2,y2)。这个性质可以用数学归纳法证明(搞ACM嘛。。。就不要那么多复杂的证明了,能用就好,匿了)

于是就变成四个变量推来推去的问题,用矩阵乘法便可以迅速解决,具体矩阵的构造可以看LS,方法有很多种。最后按给定的点把递推分成八个部分去做。

注意题目中该用long long就果断用。。。

最后贴上自己巨土的代码
{{{
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<cstring>
#include<algorithm>
struct matrix{long long a[5][5];};
struct node{long long x,y,k;};
node c[9];
matrix origin,s,bi;
const long long mod = 1234567890;
long long n,k,i,v;
using namespace std;

matrix mul(matrix a,matrix b)
{
    matrix c;
    memset(c.a,0,sizeof(c.a));
    int i,j,k;
    for (i=1;i<=4;i++)
        for (j=1;j<=4;j++)
            for (k=1;k<=4;k++)
            {
                c.a[i][j]=(c.a[i][j]+ a.a[i][k]*b.a[k][j] ) % mod;
            }
    return c;
}

matrix cnt(matrix& k,long long s)
{
    if (s == 1) return k;
    if (!s) return bi;
    matrix x=cnt(k,s/2);
    if (s%2) return mul(mul(x,x),origin);
    else return mul(x,x);
}

bool cmp(node a,node b)
{
    return a.k < b.k;
}

int main()
{
    while (cin >> n >> k >> v)
    {
        for (i=1;i<=8;i++) cin >> c[i].k >> c[i].x >> c[i].y;      
        sort(c+1,c+9,cmp);
    //  for (i=1;i<=8;i++) cout << c[i].k << " " << c[i].x << " " << c[i].y << endl;
        origin.a[1][1]=1;
        origin.a[1][2]=1;
        origin.a[1][3]=1;
        origin.a[1][4]=0;
        origin.a[2][1]=(k-1)% mod;
        origin.a[2][2]=(k-1) % mod;
        origin.a[2][3]=0;
        origin.a[2][4]=1;
        origin.a[3][1]=(v-1)% mod;
        origin.a[3][2]=0;
        origin.a[3][3]=(v-1) % mod;
        origin.a[3][4]=1;
        origin.a[4][1]=0;
        origin.a[4][2]=(v-1) % mod;
        origin.a[4][3]=(k-1) % mod;
        origin.a[4][4]=(k+v-3) % mod;
        bi.a[1][1]=1;
        bi.a[2][2]=1;
        bi.a[3][3]=1;
        bi.a[4][4]=1;
        matrix ss;
        long long ans=1;
        for (i=1;i<8;i++)
        {
    //      cout << ans << endl;
            ss=cnt(origin,c[i+1].k-c[i].k);
            if (c[i+1].x == c[i].x && c[i+1].y == c[i].y)
            {
                ans=ans*ss.a[1][1] % mod;
            }
            else if (c[i+1].x == c[i].x)
            {
                ans=ans*ss.a[1][3] % mod;
            }
            else if (c[i+1].y == c[i].y)
            {
                ans=ans*ss.a[1][2] % mod;
            }
            else 
            {
                ans=ans*ss.a[1][4] % mod;
            }
        } // end for        
            ss=cnt(origin,c[1].k-c[i].k+n);         
            if (c[1].x == c[i].x && c[1].y == c[i].y)
            {
                ans=ans*ss.a[1][1] % mod;
            }
            else if (c[1].x == c[i].x)
            {
                ans=ans*ss.a[1][3] % mod;
            }
            else if (c[1].y == c[i].y)
            {
                ans=ans*ss.a[1][2] % mod;
            }
            else 
            {
                ans=ans*ss.a[1][4] % mod;
            }           
            cout << ans % mod << endl;      
    }
    return 0;
}


}}}

占by ZhouYuChen

题意是说,一个正多边形有N个点,每个点有2个属性值(1..P,1..Q),相邻两个点必须有一种属性相同。

给出8个点的属性,求样的正多边形数量。

首先要判掉一些可能的trick,比如8个点有重,给的点有直接相邻,P=1,或 Q=1,那么答案是另一个维度Q(P)的(N-(确定点个数的次方))次方。

然后可以在两端确定的情况下,计算中间不确定的点的数量。

然后根据属性值的在1..P,1..Q 任意值都是等价的,只要处理,两端属性是否有若干维相同就可以。

比如一端是(A,B),中间有L个待填。用x,y表示一个状态。x != A ,y != B.

那么另一端和中间的,可表示成 xy ,xB ,Ay,AB,这段可一根据他的状态表示成一个列矩阵M[0],比如Ay就是 [0 0 1 0]T,表示在各种状态下的数量。

那么就可以如此题目的约束写出递推阵。

M[n+1] = D * M[n]

其中D =

[

  P+Q-3 Q-1 P-1 0
  1     P-1  0  P-1
  1     0  Q-1 Q-1
  0     1   1   1

]

比如n+1状态是AB 上一个只可能是Ay xB AB : (AB)_(n+1) = (Ay)_(n) + (xB)_(n) + (AB)_(n),

如果是 xB 上一个是 xy xB AB x有P-1种,于是 (xB)_(n+1) = (xy)_(n)*(P-1) + (xB)_(n)*(P-1) + (AB)_(n).

检查矩阵是否正确,要算每一列,的和是不是P+Q-1。一个点与上个点可以相邻有P+Q-1中情况。

在长度为L的不缺定的情况下,第L个的状态是M[L] = D^L * M[0];

然后看端点AB,答案就是 (AB)_(L) +(Ay)_(L) + (xB)_(L)

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
typedef unsigned long long num;
const num M=1234567890;
num N,P,Q;
num jc[40][4][4];
num num_pow(num a,num n){
    num ret = 1;
    a%=M;
    for(;n;n>>=1){
        if(n&1)ret=ret*a%M;
        a=a*a%M;
    }
    return ret;
}
void mul(num z[][4],num x[][4],num y[][4]){
    memset(z,0,sizeof(num)*16);
    for(size_t i=0;i<4;++i)
        for(size_t j=0;j<4;++j)
            for(size_t k=0;k<4;++k)
                z[i][j]=(z[i][j]+x[i][k]*y[k][j])%M;
}
void mul_eq(num z[][4],num x[][4]){
    static num tmp[4][4]; 
    memcpy(tmp,z,sizeof(tmp));
    mul(z,tmp,x);
}
num gao(num L,bool eq1,bool eq2){
    if(L==0)
        return (eq1||eq2)?1:0;
    if(P==1||Q==1)
        return num_pow((P+Q-1)%M,L);
    static num ret[4][4];
    memset(ret,0,sizeof(ret));
    for(int i=0;i<4;++i)ret[i][i]=1;
    num t[4][4];mul(t,ret,ret);
    for(int i=0;i<40;++i)
    if(1ull<<i & L)
        mul_eq(ret,jc[i]);
    return ret[1][(eq1?2:0)+(eq2?1:0)]+ret[2][(eq1?2:0)+(eq2?1:0)]+ret[3][(eq1?2:0)+(eq2?1:0)];
}
num x[8],u[8],v[8];
num solve(){
#define mp make_pair
    for(int i=0;i<8;++i)
        scanf("%lld%lld%lld",x+i,u+i,v+i);
    map<num,pair<num,num> > ss;
    for(int i=0;i<8;++i)
        if(ss.count(x[i])){
            if(ss[x[i]]!= mp(u[i],v[i]))return 0;
        }
        else ss[x[i]]=mp(u[i],v[i]);
    int m = 0;
    num a[4][4]={
        {(P+Q-3+M)%M, (Q-1)%M,(P-1)%M,0},
        {1,(P-1)%M,0,(P-1)%M},
        {1,0,(Q-1)%M,(Q-1)%M},
        {0,1,1,1}
    };
    memcpy(jc[0],a,sizeof(a));
    for(int i =1;i<=40;++i)
        mul(jc[i],jc[i-1],jc[i-1]);
    for(__typeof(ss.begin()) it=ss.begin();it!=ss.end();++it,++m){
        x[m]=it->first;
        u[m]=it->second .first;
        v[m]=it->second .second;
    }
    num  ans = 1;
    for(size_t i=1;i<m;++i)
        ans = ans * gao(x[i]-x[i-1]-1,(u[i-1]==u[i]),(v[i-1]==v[i])) % M;
    ans =ans * gao(x[0]+N-x[m-1]-1,(u[m-1]==u[0]),(v[m-1]==v[0]))%M;
    return ans;
}
int main(){
    for(;scanf("%lld%lld%lld",&N,&P,&Q)==3;)
        printf("%lld\n",solve());
    return 0;
}


我是无辜的分割线

学长好快,仰慕学长。

题意学长已经说了,这里便不作多余阐述。

题目中一个关键条件便是相邻两点之间,两种属性至少有一个相同。利用这个条件,我们可以把问题变成,在一个K*S的棋盘上,从某点出发,每次走到同行同列的任意一个点上。求从一个点出发走N步后回到原点的方案数,当中8步一定要走到某些给定的点(有冲突的可以预处理掉,当然算法够健壮也可以直接处理掉)。不难发现(也可以人肉做几组观察)到棋盘上的点可以分成四种:起点,与起点同行不同列,与起点同列不同行,与起点不同列不同行。记走N步后走到点(x,y)的方案数为f(N,x,y),不难发现,若(x1,y1)与(x2,y2)是同类点,则 f(N,x1,y1) == f(N,x2,y2)。这个性质可以用数学归纳法证明(搞ACM嘛。。。就不要那么多复杂的证明了,能用就好,匿了)

于是就变成四个变量推来推去的问题,用矩阵乘法便可以迅速解决,具体矩阵的构造可以看LS,方法有很多种。最后按给定的点把递推分成八个部分去做。

注意题目中该用long long就果断用。。。

最后贴上自己巨土的代码

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<cstring>
#include<algorithm>
struct matrix{long long a[5][5];};
struct node{long long x,y,k;};
node c[9];
matrix origin,s,bi;
const long long mod = 1234567890;
long long n,k,i,v;
using namespace std;
matrix mul(matrix a,matrix b)
{
    matrix c;
    memset(c.a,0,sizeof(c.a));
    int i,j,k;
    for (i=1;i<=4;i++)
        for (j=1;j<=4;j++)
            for (k=1;k<=4;k++)
            {
                c.a[i][j]=(c.a[i][j]+ a.a[i][k]*b.a[k][j] ) % mod;
            }
    return c;
}
matrix cnt(matrix& k,long long s)
{
    if (s == 1) return k;
    if (!s) return bi;
    matrix x=cnt(k,s/2);
    if (s%2) return mul(mul(x,x),origin);
    else return mul(x,x);
}
bool cmp(node a,node b)
{
    return a.k < b.k;
}
int main()
{
    while (cin >> n >> k >> v)
    {
        for (i=1;i<=8;i++) cin >> c[i].k >> c[i].x >> c[i].y;      
        sort(c+1,c+9,cmp);
    //  for (i=1;i<=8;i++) cout << c[i].k << " " << c[i].x << " " << c[i].y << endl;
        origin.a[1][1]=1;
        origin.a[1][2]=1;
        origin.a[1][3]=1;
        origin.a[1][4]=0;
        origin.a[2][1]=(k-1)% mod;
        origin.a[2][2]=(k-1) % mod;
        origin.a[2][3]=0;
        origin.a[2][4]=1;
        origin.a[3][1]=(v-1)% mod;
        origin.a[3][2]=0;
        origin.a[3][3]=(v-1) % mod;
        origin.a[3][4]=1;
        origin.a[4][1]=0;
        origin.a[4][2]=(v-1) % mod;
        origin.a[4][3]=(k-1) % mod;
        origin.a[4][4]=(k+v-3) % mod;
        bi.a[1][1]=1;
        bi.a[2][2]=1;
        bi.a[3][3]=1;
        bi.a[4][4]=1;
        matrix ss;
        long long ans=1;
        for (i=1;i<8;i++)
        {
    //      cout << ans << endl;
            ss=cnt(origin,c[i+1].k-c[i].k);
            if (c[i+1].x == c[i].x && c[i+1].y == c[i].y)
            {
                ans=ans*ss.a[1][1] % mod;
            }
            else if (c[i+1].x == c[i].x)
            {
                ans=ans*ss.a[1][3] % mod;
            }
            else if (c[i+1].y == c[i].y)
            {
                ans=ans*ss.a[1][2] % mod;
            }
            else 
            {
                ans=ans*ss.a[1][4] % mod;
            }
        } // end for        
            ss=cnt(origin,c[1].k-c[i].k+n);         
            if (c[1].x == c[i].x && c[1].y == c[i].y)
            {
                ans=ans*ss.a[1][1] % mod;
            }
            else if (c[1].x == c[i].x)
            {
                ans=ans*ss.a[1][3] % mod;
            }
            else if (c[1].y == c[i].y)
            {
                ans=ans*ss.a[1][2] % mod;
            }
            else 
            {
                ans=ans*ss.a[1][4] % mod;
            }           
            cout << ans % mod << endl;      
    }
    return 0;
}