2012-A3-0017
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
题意是说,一个正多边形有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;
}
}}}
{{{
学长你的矩阵D我还是没有看明白。。M矩阵是[xy ,xB ,Ay,AB]的情况下,我推出来的矩阵D和猛犸学长一样,是
{p+q-3, 1 , 1 ,0},
{ q-1 ,p-1, 0 ,1},
{ p-1 , 0 ,q-1,1},
{ 0 ,p-1,q-1,1},
求解释@.@
比如xB转移到xy的情况,由于B变成y肯定导致不同,所以x一定要相同,因此有q-1种变法,为什么学长的矩阵里是1?
by 大肥羊
}}}
{{{区别在计算时变换矩阵是左乘还是右乘}}}
我的习惯比较倾向于 D^n^×X,X是个列向量,这样和很多教材中的表达方式一致
你给的矩阵应该是一个行向量Y×E^n^这样。而这样表述的话,D=E^T^,X=Y^T^,二者表达的意思应该是一样的。
在我的矩阵里xB变成xy的情况应该是看第一行的第2列,也是Q-1.
{{{
啊~我明白了~果然还是我对矩阵乘法的理解不扎实@.@ by大肥羊
}}}
题意是说,一个正多边形有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] = DL * 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;
}
学长你的矩阵D我还是没有看明白。。M矩阵是[xy ,xB ,Ay,AB]的情况下,我推出来的矩阵D和猛犸学长一样,是
{p+q-3, 1 , 1 ,0},
{ q-1 ,p-1, 0 ,1},
{ p-1 , 0 ,q-1,1},
{ 0 ,p-1,q-1,1},
求解释@.@
比如xB转移到xy的情况,由于B变成y肯定导致不同,所以x一定要相同,因此有q-1种变法,为什么学长的矩阵里是1?
by 大肥羊
区别在计算时变换矩阵是左乘还是右乘
我的习惯比较倾向于 Dn×X,X是个列向量,这样和很多教材中的表达方式一致
你给的矩阵应该是一个行向量Y×En这样。而这样表述的话,D=ET,X=YT,二者表达的意思应该是一样的。
在我的矩阵里xB变成xy的情况应该是看第一行的第2列,也是Q-1.
啊~我明白了~果然还是我对矩阵乘法的理解不扎实@.@ by大肥羊