概况
今日复习线性dp,约数,快速幂
约数
求约数
vector<int> get_divisors(int n){
vector<int> res;
for(int i=1;i<=n/i;i++){
if(n%i==0)P
res.push_back(i);
if(i!=n/i) res.push_back(n/i);//判断另一个约数是不是和i相等
}
sort(res.begin(),res.end());
return res;
}
求约数个数
由于每个数都可以被形式都是自己的约数的幂次相乘表示
所以约数的个数就是每个[0,幂次]的排列组合,也就是(幂次+1)种选法相乘
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
int main(){
int n;
cin>>n;
unordered_map<int,int> hs;
while(n--){
int x;cin>>x;
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
hs[i]++;
}
}
if(x>1) hs[x]++;//剩下最后一个约数也加入
}
ll ans=1;
for(auto i:hs)//对哈希表的范围遍历得到元素是pair键值对类型
ans=ans*(i.second+1)%mod;
cout<<ans;
return 0;
}
求约数之和
约数之和可以化成各个底数的0-最高次幂的和的乘积
在上方代码将各个数的存入哈希表的基础上求约数之和可以化为
ll ans=1;
for(auto p:hs){
int a=p.first,b=p.second;
ll t=1;
while(b--) t=(t*a+1)%mod;//这边就是当前底数的各幂次之和
ans=ans*t%mod;//同余定理
}
cout<<ans;
快速幂(参考链接)
其实就是求a的k次方,把k化成2的底数次幂相加的结果,从而联系数的二进制表示,通过预处理的这些a的2底数次幂将对应位为1的数相乘即可
#include <iostream>
using namespace std;
typedef long long ll;
ll qmi(int a,int k,int p){//a的k次方modp
ll res=1,t=a;
while(k){
if(k&1) res=(res*t)%p;
k>>=1;//右移
t=t*t%p;
}
return res;
}
int main(){
int n;
cin>>n;
while(n--){
int a,k,p;
cin>>a>>k>>p;
cout<<qmi(a,k,p)<<endl;
}
}
P2196 [NOIP1996 提高组] 挖地雷
此题类似于最长上升子序列,唯一不同的是其中多了是否联通这个条件,判断这个条件可以用邻接表或者邻接矩阵,再用一个数组存当前该点是从哪个点走过来的,在用递归倒着输出路径即可
题目描述
在一个地图上有 (N≤20) 个地窖,每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径。当地窖及其连接的数据给出之后,某人可以从任一处开始挖地雷,然后可以沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使某人能挖到最多的地雷。
输入格式
有若干行。
第 1 行只有一个数字,表示地窖的个数 N。
第 2 行有 N 个数,分别表示每个地窖中的地雷个数。
第 3 行至第 N+1 行表示地窖之间的连接情况:
第 3 行有 n−1 个数(0 或 1),表示第一个地窖至第 2 个、第 3 个 …… 第 n 个地窖有否路径连接。如第 3 行为 11000⋯0,则表示第 1 个地窖至第 2 个地窖有路径,至第 3 个地窖有路径,至第 4 个地窖、第 5 个 …… 第 n 个地窖没有路径。
第 4 行有 n−2 个数,表示第二个地窖至第 3 个、第 4 个 …… 第 n 个地窖有否路径连接。
第 n+1 行有 1 个数,表示第 n−1 个地窖至第 n 个地窖有否路径连接。(为 0 表示没有路径,为 1 表示有路径)。
输出格式
第一行表示挖得最多地雷时的挖地雷的顺序,各地窖序号间以一个空格分隔,不得有多余的空格。
第二行只有一个数,表示能挖到的最多地雷数。
#include <bits/stdc++.h>
using namespace std;
const int N=21;
int h[N],e[N],ne[4*N],idx,w[N],n;
int f[N],path[N];//挖到i号地窖停止的最大地雷数
void add(int a,int b){
e[idx]=b;//加b点
ne[idx]=h[a];
h[a]=idx++;
}
bool isedge(int a,int b){//判断有边
for(int i=h[a];i!=-1;i=ne[i]){
if(e[i]==b) return true;
}
return false;
}
void dfs(int i){
if(path[i]) dfs(path[i]);
cout<<i<<' ';
}
void solve(){//参考最长上升子序列
int ans=0,pos=0;
for(int i=1;i<=n;i++){
f[i]=w[i];
for(int j=1;j<i;j++)
if(isedge(j,i)&&f[i]<f[j]+w[i]){
f[i]=f[j]+w[i];
path[i]=j;
}
if(ans<f[i]){
ans=f[i];//记录最大数量
pos=i;//记录终止的下标
}
}
dfs(pos);//倒着输出路径
cout<<'\n'<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
memset(h,-1,sizeof h);
cin>>n;
for(int i=1;i<=n;i++){
cin>>w[i];
}
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++){
int op;cin>>op;
if(op==1) add(i,j);
}
solve();
return 0;
}