今日总结2024/3/31

概况

今日复习线性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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值