其实我对原理不是很了解,对最后一行前缀和和B数组也不是很精,不过。。能做出来就好。。能凑对就好。。。
克拉克是一名人格分裂患者。某一天,克拉克变成了一个研究人员,在研究数字。 他想知道在所有长度在[l,r]之间的能被7整除且相邻数位之和不为k的正整数有多少个。
第一行一个整数T(1≤T≤5),表示数据的组数。 每组数据只有一行三个整数l,r,k(1≤l≤r≤109,0≤k≤18)。
每组数据输出一行一个数,表示答案。由于答案太大,你只需对109+7取模即可。
2 1 2 5 2 3 5
13 125
第一个样例有13个数满足,分别是:7,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; }