矩阵快速幂【集合】
2016-02-06 16:54
183 查看
其实我对原理不是很了解,对最后一行前缀和和B数组也不是很精,不过。。能做出来就好。。能凑对就好。。。
问题描述
输入描述
输出描述
输入样例
输出样例
Hint
定义dp[i][j][k]表示位数为i,当前数%7的值而且以k结尾的方案数。
先列出dp方程,dp[i][x][ (t*10+x)%7 ]+=dp[i-1][j][t],因为i太大,所以要用矩阵快速幂加速。
[Topcoder]给出n,k,计算 1k +
2k + 3k +
... + nk modulo 1000000007.
要实现求和长度在k左右的递推式(n+1)^k-n^k=sigma(c[k][i]*n^i);
b个堆,每个堆有n个数字,每一个堆取一个数字,第i个堆取一个数字做第i位的数字,构成一个n位数,求构成的数字被x除余k有多少种方案。
http://codeforces.com/contest/621/problem/E
【快速幂变形】CD484C 一般做法会超时,但很难想到快速幂
//思路:首先 他是对1到k 元素做一次变换,然后对2到k+1个元素做一次变化。。。。依次做完。
// 如果我们对1到k个元素做完一次变换后,把整个数组循环左移一个。
// 那么第二次还是对1 到 k个元素做和第一次一样的变换,再左移,
// 再对1 到 k个元素做和第一次一样的变换,依次做完n-k+1即可。
// 假设题目要求的变换为C 循环左移变换为P。那么对于每次查询 相当于做 n-k+1 CP)变换。
// 最后把答案再向右移动k-1 回到原来位置即可。
// 那么问题就解决了 效率 每次查询n log(n-k+1)
#include<bits/stdc++.h>
using namespace std;
char s[1000005];
char a[1000005];
int p[1000005];
int ans[1000005];
int tmp[1000005];
int main(){
int n,m;
scanf("%s",s);
n=strlen(s);
scanf("%d",&m);
for(int i=0;i<m;++i){
int k,d;
scanf("%d%d",&k,&d);
for(int j=0;j<n;++j)
p[j]=ans[j]=j;
int c=0;
for(int x=0;x<d;++x)
for(int j=x;j<k;j+=d) {
p[c++]=j; //这个下标是由第几个数存放
// p[j]=x*d+j/d; //第j个数最后被保存的下标(每次取前K个下标操作,这样不好取)
}
int last=p[0];
for(int j=0;j <n-1;++j)
p[j]=p[j+1]; //对1到k个元素做完一次变换后,把整个数组循环左移一个
p[n-1]=last;
int x=n-k +1;
while(x){
if(x&1){
for(int j=0;j<n;++j)
tmp[j]=ans[p[j]]; //这个下标是由第几个数存放
for(int j=0;j<n;++j)
ans[j]=tmp[j];//021345,类似p[p[]]的操作,区别是在p变了几次后集中处理一次
}
for(int j=0;j<n;++j)
tmp[j]=p[p[j]];
for(int j=0;j<n;++j)
p[j]=tmp[j];
x >>= 1;
}
for(int j=0;j<n;++j) {
a[j]=s[ans[(j+k-1)%n]];
}
printf("%s\n",a);
for(int j=0;j<n;++j) {
s[j]=a[j]; //传递字符串a到s
}
}
return 0;
}
【poj3613】从s到e恰好经过n条边的最短路(可以有重边)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int N,T,S,E,num;
map<int,int> mp;
struct Matrix{
int ma[210][210];
void clear(){
memset(ma,0x3f,sizeof(ma));//初始化一定要大,否则WA
}
};
Matrix Floyd(Matrix a,Matrix b){
Matrix dis;
dis.clear();
int i,j,k;
for(k=1;k<=num;k++) //一次floyd 是找到一个中间点
for(i=1;i<=num;i++)
for(j=1;j<=num;j++)
if(dis.ma[i][j]>a.ma[i][k]+b.ma[k][j])
dis.ma[i][j]=a.ma[i][k]+b.ma[k][j];
return dis;
}
Matrix Solve(Matrix a,int k){
Matrix ans=a;
while(k){
if(k&1){
ans=Floyd(ans,a);
}
a=Floyd(a,a);
k>>=1;
}
return ans;
}
int main(){
Matrix a;
while(~scanf("%d%d%d%d",&N,&T,&S,&E)){
num=0;
mp.clear();
a.clear();
int u,v,w;
while(T--){
scanf("%d%d%d",&w,&u,&v);
if(mp[u]==0)
mp[u]=++num;
if(mp[v]==0)
mp[v]=++num;
if(a.ma[mp[u]][mp[v]]>w)
a.ma[mp[u]][mp[v]]=a.ma[mp[v]][mp[u]]=w;
}
a=Solve(a,N-1); // N 条边 ,经过 N-1 个点
printf("%d\n",a.ma[mp[S]][mp[E]]);
}
return 0;
}
问题描述
克拉克是一名人格分裂患者。某一天,克拉克变成了一个研究人员,在研究数字。 他想知道在所有长度在[l, r][l,r]之间的能被77整除且相邻数位之和不为kk的正整数有多少个。
输入描述
第一行一个整数T(1 \le T \le 5)T(1≤T≤5),表示数据的组数。 每组数据只有一行三个整数l, r, k(1 \le l \le r \le 10^9, 0 \le k \le 18)l,r,k(1≤l≤r≤109,0≤k≤18)。
输出描述
每组数据输出一行一个数,表示答案。由于答案太大,你只需对10^9+7109+7取模即可。
输入样例
2 1 2 5 2 3 5
输出样例
13 125
Hint
第一个样例有13个数满足,分别是:7,21,28,35,42,49,56,63,70,77,84,91,987,21,28,35,42,49,56,63,70,77,84,91,98
定义dp[i][j][k]表示位数为i,当前数%7的值而且以k结尾的方案数。
先列出dp方程,dp[i][x][ (t*10+x)%7 ]+=dp[i-1][j][t],因为i太大,所以要用矩阵快速幂加速。
#include <bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; typedef vector<long long> vec; typedef vector<vec> mat; mat mul(mat &A,mat &B) { mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) { for(int k=0;k<B.size();k++) { for(int j=0;j<B[0].size();j++) { C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; } } } return C; } mat pow(mat A,long long n) { mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();++i) B[i][i]=1; while(n>0) { if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } int main() { int t; cin>>t; while(t--) { int l,r,m; cin>>l>>r>>m; mat A(71+2,vec(71+2)); mat AA(71, vec(1)); mat BB(71, vec(1)); for(int i=0;i<=9;++i){ //上一次的末位 for(int j=0;j<=9;++j){ //这一次的末位 if(i+j==m) continue; for(int k=0;k<7;++k){ //上一次的余7后的余数 A[(k*10+j)%7*10+j][k*10+i]++; //当前状态和前一个状态,将余数和末位合并起来存储 } } } for(int i=0;i<=9;++i) //求前缀和必加,本题较于下题加这个的原因在于长度可以是1~l/1~r,下题只能是固定长度 A[70][i]=1; //求前缀和必加,后面这个i(0~9)是指最终状态可能性(余数0,末位0~9) A[70][70]=1; //求前缀和必加 AA=pow(A,l-1); BB=pow(A,r); ll s1=0,s2=0; for(int i=1;i<=9;++i){ s1+=AA[70][(i%7)*10+i]; //初始状态不是0而是特殊处理,是因为首位不能为0 s2+=BB[70][(i%7)*10+i]; } cout<<(s2-s1+mod)%mod<<endl; } return 0; }
[Topcoder]给出n,k,计算 1k +
2k + 3k +
... + nk modulo 1000000007.
要实现求和长度在k左右的递推式(n+1)^k-n^k=sigma(c[k][i]*n^i);
#include<bits/stdc++.h> #define ll long long #define MOD 1000000007 using namespace std; ll CC[55][55]; void permut(){ CC[0][0]=1; for(int i=1;i<=50;++i){ CC[i][0]=1; CC[i][i]=1; } for(int i=2;i<=50;++i){ for(int j=1;j<i;++j){ CC[i][j]=(CC[i-1][j]+CC[i-1][j-1])%MOD; } } } typedef vector<ll> vec; typedef vector<vec> mat; mat mul(mat &A,mat &B){ mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++){ for(int k=0;k<B.size();k++){ for(int j=0;j<B[0].size();j++){ C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%MOD)%MOD; } } } return C; } mat pow(mat A,ll n){ mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();++i) B[i][i]=1; while(n>0){ if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } int main() { int t; cin>>t; permut(); while(t--){ int n,k; cin>>n>>k; mat A(k+2,vec(k+2)); mat B(k+2, vec(1)); for(int i=0;i<=k;++i){ for(int j=0;j<=i;++j){ A[i][j]=CC[i][j]; //i次方项的系数是由Σ((0<=j<=i)次方项乘上C(i,j))得出 } } //求前缀和必加 for(int i=0;i<=k;++i) A[k+1][i]=CC[k][i]; //这一步不理解(为什么加的是CC[k][i]) A[k+1][k+1]=1; B[0][0]=1; A=pow(A,n); B=mul(A,B); cout<<B[k+1][0]<<endl; } return 0; }
b个堆,每个堆有n个数字,每一个堆取一个数字,第i个堆取一个数字做第i位的数字,构成一个n位数,求构成的数字被x除余k有多少种方案。
http://codeforces.com/contest/621/problem/E
#include <bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; typedef vector<long long> vec; typedef vector<vec> mat; mat mul(mat &A,mat &B) { mat C(A.size(),vec(B[0].size())); for(int i=0;i<A.size();i++) { for(int k=0;k<B.size();k++) { for(int j=0;j<B[0].size();j++) { C[i][j]=(C[i][j]+(A[i][k]*B[k][j])%mod)%mod; } } } return C; } mat pow(mat A,long long n) { mat B(A.size(),vec(A.size())); for(int i=0;i<A.size();++i) B[i][i]=1; while(n>0) { if(n&1) B=mul(B,A); A=mul(A,A); n>>=1; } return B; } int num[11]; int main(){ memset(num,0,sizeof(num)); int n,b,k,x,a; cin>>n>>b>>k>>x; for(int i=1;i<=n;++i){ cin>>a; num[a]++; } mat A(x+2,vec(x+2)); mat B(x+2, vec(1)); for(int i=0;i<x;++i){ //上一次%x后的余数 for(int j=1;j<=9;++j){ //这一次的末位 A[(i*10+j)%x][i]+=num[j]; //j这个数字在数组中出现的次数 } } // for(int i=0;i<=9;++i) //求前缀和必加 // A[x][i]=num[i]; //求前缀和必加 // A[x][x]=1; //求前缀和必加 A=pow(A,b); cout<<A[k][0]<<endl; return 0; }
【快速幂变形】CD484C 一般做法会超时,但很难想到快速幂
//思路:首先 他是对1到k 元素做一次变换,然后对2到k+1个元素做一次变化。。。。依次做完。
// 如果我们对1到k个元素做完一次变换后,把整个数组循环左移一个。
// 那么第二次还是对1 到 k个元素做和第一次一样的变换,再左移,
// 再对1 到 k个元素做和第一次一样的变换,依次做完n-k+1即可。
// 假设题目要求的变换为C 循环左移变换为P。那么对于每次查询 相当于做 n-k+1 CP)变换。
// 最后把答案再向右移动k-1 回到原来位置即可。
// 那么问题就解决了 效率 每次查询n log(n-k+1)
#include<bits/stdc++.h>
using namespace std;
char s[1000005];
char a[1000005];
int p[1000005];
int ans[1000005];
int tmp[1000005];
int main(){
int n,m;
scanf("%s",s);
n=strlen(s);
scanf("%d",&m);
for(int i=0;i<m;++i){
int k,d;
scanf("%d%d",&k,&d);
for(int j=0;j<n;++j)
p[j]=ans[j]=j;
int c=0;
for(int x=0;x<d;++x)
for(int j=x;j<k;j+=d) {
p[c++]=j; //这个下标是由第几个数存放
// p[j]=x*d+j/d; //第j个数最后被保存的下标(每次取前K个下标操作,这样不好取)
}
int last=p[0];
for(int j=0;j <n-1;++j)
p[j]=p[j+1]; //对1到k个元素做完一次变换后,把整个数组循环左移一个
p[n-1]=last;
int x=n-k +1;
while(x){
if(x&1){
for(int j=0;j<n;++j)
tmp[j]=ans[p[j]]; //这个下标是由第几个数存放
for(int j=0;j<n;++j)
ans[j]=tmp[j];//021345,类似p[p[]]的操作,区别是在p变了几次后集中处理一次
}
for(int j=0;j<n;++j)
tmp[j]=p[p[j]];
for(int j=0;j<n;++j)
p[j]=tmp[j];
x >>= 1;
}
for(int j=0;j<n;++j) {
a[j]=s[ans[(j+k-1)%n]];
}
printf("%s\n",a);
for(int j=0;j<n;++j) {
s[j]=a[j]; //传递字符串a到s
}
}
return 0;
}
【poj3613】从s到e恰好经过n条边的最短路(可以有重边)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int N,T,S,E,num;
map<int,int> mp;
struct Matrix{
int ma[210][210];
void clear(){
memset(ma,0x3f,sizeof(ma));//初始化一定要大,否则WA
}
};
Matrix Floyd(Matrix a,Matrix b){
Matrix dis;
dis.clear();
int i,j,k;
for(k=1;k<=num;k++) //一次floyd 是找到一个中间点
for(i=1;i<=num;i++)
for(j=1;j<=num;j++)
if(dis.ma[i][j]>a.ma[i][k]+b.ma[k][j])
dis.ma[i][j]=a.ma[i][k]+b.ma[k][j];
return dis;
}
Matrix Solve(Matrix a,int k){
Matrix ans=a;
while(k){
if(k&1){
ans=Floyd(ans,a);
}
a=Floyd(a,a);
k>>=1;
}
return ans;
}
int main(){
Matrix a;
while(~scanf("%d%d%d%d",&N,&T,&S,&E)){
num=0;
mp.clear();
a.clear();
int u,v,w;
while(T--){
scanf("%d%d%d",&w,&u,&v);
if(mp[u]==0)
mp[u]=++num;
if(mp[v]==0)
mp[v]=++num;
if(a.ma[mp[u]][mp[v]]>w)
a.ma[mp[u]][mp[v]]=a.ma[mp[v]][mp[u]]=w;
}
a=Solve(a,N-1); // N 条边 ,经过 N-1 个点
printf("%d\n",a.ma[mp[S]][mp[E]]);
}
return 0;
}
相关文章推荐
- Android ListView 的优化使用
- Android Wear Develope Guide 1
- 一般的hibernate查询语句
- Linux 常见命令小技巧集锦
- HTTP协议的理解
- Python模拟登录验证码(代码简单)
- C++基本概念——你所不知道的sizeof运算符
- 开灯问题
- 在ubuntu上安装zeppelin
- linux mtr 命令详解
- 我的2015
- 我的2015
- Xcode 6制作动态及静态Framework
- 栈
- iOS开发之静态库.a的制作教程
- 运行Tomcat/Weblogic发生OutOfMemoryError: PermGen space错误
- 人员角色管理
- ember.js的render过程分析
- HD1847 Good Luck in CET-4 Everybody!(巴什博弈)
- 解读ember的应用模型