team2012-E1-mysol-0017
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
/* 解题报告 //
给出一个 N 边形,其中每个顶点都有两个值 U[i] 和 V[i]
且满足 1 <= U[i] <= P,1 <= V[i] <= Q,相邻的两个顶点至少有一个值相同
再给出八个已知条件,表示第 X[i] 个点的值一定是 U[X[i]] 和 V[X[i]]
求在这种限制条件下,不同的 U[i] 和 V[i] 组合的方案数
如果没有限制条件,很明显就是简单的公式题
有了限制条件之后,只能考虑用矩阵做递推,现在考虑 X[i] 至 X[i + 1] 区间
令每个向量 a[p] 有四个元素,表示在位置 p 的方案数,四个元素分别是
0: U 的值和 U[X[i + 1]] 不同,V 的值和 V[X[i + 1]] 不同的方案数
1: U 的值和 U[X[i + 1]] 不同,V 的值和 V[X[i + 1]] 相同的方案数
2: U 的值和 U[X[i + 1]] 相同,V 的值和 V[X[i + 1]] 不同的方案数
3: U 的值和 U[X[i + 1]] 相同,V 的值和 V[X[i + 1]] 相同的方案数
转移矩阵是 4 * 4 的,根据所列的向量 a 的四个元素的意义进行转移
那 16 个值写起来比较麻烦就省略了,大家可以参考代码里写的值,去推一推
总之,有了初始向量和转移矩阵后,直接矩阵快速幂就搞定了
当时验题时我写的是 9 * 9 的转移矩阵,验到早上 9 点才验出来...
// By 猛犸也钻地 @ 2012.07.22 */
}}}
{{{
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class MatInt{
public:
static const int MOD = 1234567890;
typedef long long LL;
typedef vector<int> BAR;
int n,m;
vector<BAR> u;
MatInt():n(),m(){}
MatInt(int c):u(c,BAR(c)){for(n=m=c;c--;u[c][c]=1);}
MatInt(int n, int m):n(n),m(m),u(n,BAR(m)){}
BAR& operator [](int idx){return u[idx];}
const BAR& operator [](int idx) const{return u[idx];}
operator bool() const{return n && m;}
MatInt operator ~() const{
MatInt ret(m,n);
for(int i=0;i<m;i++) for(int j=0;j<n;j++) ret[i][j]=u[j][i];
return ret;
}
MatInt operator *(const MatInt& c) const{
if(m!=c.n) return MatInt();
MatInt ret(n,c.m),v=~c;
for(int i=0;i<n;i++) for(int j=0;j<c.m;j++) for(int k=0;k<m;k++)
ret[i][j]=(ret[i][j]+LL(u[i][k])*v[j][k])%MOD;
return ret;
}
MatInt& operator *=(const MatInt& c){return *this=*this*c;}
MatInt pow(long long e) const{
if(n!=m) return MatInt();
MatInt ret=n,c=*this;
for(;e;e>>=1,c*=c) if(e&1) ret*=c;
return ret;
}
};
int main(){
long long n,p,q,x[9],u[9],v[9];
while(scanf("%lld%lld%lld",&n,&p,&q)==3){
int m=8,ans=1;
for(int i=0;i<m;i++){
scanf("%lld%lld%lld",x+i,u+i,v+i);
for(int j=0;j<i;j++) if(x[i]<x[j]){
swap(x[i],x[j]);
swap(u[i],u[j]);
swap(v[i],v[j]);
}
}
x[m]=x[0]+n,u[m]=u[0],v[m]=v[0];
for(int i=0;i<m;i++){
MatInt a(1,4),e(4,4);
const long long r[4][4]={
{p+q-3, 1 , 1 ,0},
{ q-1 ,p-1, 0 ,1},
{ p-1 , 0 ,q-1,1},
{ 0 ,p-1,q-1,1},
};
for(int j=0;j<4;j++) for(int k=0;k<4;k++)
e[j][k]=r[j][k]%e.MOD;
a[0][(u[i]==u[i+1])*2|(v[i]==v[i+1])]=ans;
a*=e.pow(x[i+1]-x[i]);
ans=a[0][3];
}
printf("%d\n",ans);
}
}
}}}
/* 解题报告 //
给出一个 N 边形,其中每个顶点都有两个值 U[i] 和 V[i]
且满足 1 <= U[i] <= P,1 <= V[i] <= Q,相邻的两个顶点至少有一个值相同
再给出八个已知条件,表示第 X[i] 个点的值一定是 U[X[i]] 和 V[X[i]]
求在这种限制条件下,不同的 U[i] 和 V[i] 组合的方案数
如果没有限制条件,很明显就是简单的公式题
有了限制条件之后,只能考虑用矩阵做递推,现在考虑 X[i] 至 X[i + 1] 区间
令每个向量 a[p] 有四个元素,表示在位置 p 的方案数,四个元素分别是
0: U 的值和 U[X[i + 1]] 不同,V 的值和 V[X[i + 1]] 不同的方案数
1: U 的值和 U[X[i + 1]] 不同,V 的值和 V[X[i + 1]] 相同的方案数
2: U 的值和 U[X[i + 1]] 相同,V 的值和 V[X[i + 1]] 不同的方案数
3: U 的值和 U[X[i + 1]] 相同,V 的值和 V[X[i + 1]] 相同的方案数
转移矩阵是 4 * 4 的,根据所列的向量 a 的四个元素的意义进行转移
那 16 个值写起来比较麻烦就省略了,大家可以参考代码里写的值,去推一推
总之,有了初始向量和转移矩阵后,直接矩阵快速幂就搞定了
当时验题时我写的是 9 * 9 的转移矩阵,验到早上 9 点才验出来...
// By 猛犸也钻地 @ 2012.07.22 */
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class MatInt{
public:
static const int MOD = 1234567890;
typedef long long LL;
typedef vector<int> BAR;
int n,m;
vector<BAR> u;
MatInt():n(),m(){}
MatInt(int c):u(c,BAR(c)){for(n=m=c;c--;u[c][c]=1);}
MatInt(int n, int m):n(n),m(m),u(n,BAR(m)){}
BAR& operator [](int idx){return u[idx];}
const BAR& operator [](int idx) const{return u[idx];}
operator bool() const{return n && m;}
MatInt operator ~() const{
MatInt ret(m,n);
for(int i=0;i<m;i++) for(int j=0;j<n;j++) ret[i][j]=u[j][i];
return ret;
}
MatInt operator *(const MatInt& c) const{
if(m!=c.n) return MatInt();
MatInt ret(n,c.m),v=~c;
for(int i=0;i<n;i++) for(int j=0;j<c.m;j++) for(int k=0;k<m;k++)
ret[i][j]=(ret[i][j]+LL(u[i][k])*v[j][k])%MOD;
return ret;
}
MatInt& operator *=(const MatInt& c){return *this=*this*c;}
MatInt pow(long long e) const{
if(n!=m) return MatInt();
MatInt ret=n,c=*this;
for(;e;e>>=1,c*=c) if(e&1) ret*=c;
return ret;
}
};
int main(){
long long n,p,q,x[9],u[9],v[9];
while(scanf("%lld%lld%lld",&n,&p,&q)==3){
int m=8,ans=1;
for(int i=0;i<m;i++){
scanf("%lld%lld%lld",x+i,u+i,v+i);
for(int j=0;j<i;j++) if(x[i]<x[j]){
swap(x[i],x[j]);
swap(u[i],u[j]);
swap(v[i],v[j]);
}
}
x[m]=x[0]+n,u[m]=u[0],v[m]=v[0];
for(int i=0;i<m;i++){
MatInt a(1,4),e(4,4);
const long long r[4][4]={
{p+q-3, 1 , 1 ,0},
{ q-1 ,p-1, 0 ,1},
{ p-1 , 0 ,q-1,1},
{ 0 ,p-1,q-1,1},
};
for(int j=0;j<4;j++) for(int k=0;k<4;k++)
e[j][k]=r[j][k]%e.MOD;
a[0][(u[i]==u[i+1])*2|(v[i]==v[i+1])]=ans;
a*=e.pow(x[i+1]-x[i]);
ans=a[0][3];
}
printf("%d\n",ans);
}
}