202312-1 仓库规划
西西艾弗岛上共有n个仓库,依次编号为1…n。每个仓库均有一个m维向量的位置编码,用来表示仓库间的物流运转关系。
具体来说,每个仓库i均可能有一个上级仓库j,满足:仓库j位置编码的每一维均大于仓库i位置编码的对应元素。比如编码为(1,1,1)的仓库可以成为(0,0,0)的上级,但不能成为(0,1,0) 的上级。如果有多个仓库均满足该要求,则选取其中编号最小的仓库作为仓库i的上级仓库;如果没有仓库满足条件,则说明仓库i是一个物流中心,没有上级仓库。
现给定n个仓库的位置编码,试计算每个仓库的上级仓库编号。
#include <bits/stdc++.h>
using namespace std;
bool cmp(vector<int> a1, vector<int> a2, int len) {
for (int i = 0; i < len; i++) {
if (a1[i] >= a2[i])
return false;
}
return true;
}
int main(){
int n,m;
cin>>n>>m;
vector<vector<int>> vec(n,vector<int>(m,0));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin>>vec[i][j];
}
}
for(int i=0; i<n; i++) {
bool isFind = false;
for (int j = 0; j < n; j++) {
if (cmp(vec[i], vec[j], m)) {
cout << j + 1 << endl;
isFind = true;
break;
}
}
if (isFind == false)
cout << 0 << endl;
}
return 0;
}
202312-2 因子化简
小P同学在学习了素数的概念后得知,任意的正整数n都可以唯一地表示为若干素因子相乘的形式。如果正整数n有m个不同的素数因子p1,P2,…,Pm,则可以表示为:n=pxpx…xpm。
小P认为,每个素因子对应的指数t;反映了该素因子对于几的重要程度。现设定一个阈值k,如果某个素因子p;对应的指数t;小于k,则认为该素因子不重要,可以将书项从几中除去;反之则将p;项保留。最终剩余项的乘积就是n简化后的值,如果没有剩余项则认为简化后的值等于1。
试编写程序处理q个查询:
每个查询包含两个正整数n和k,要求计算按上述方法将n简化后的值。
#include<bits/stdc++.h>
using namespace std;
bool isPrime(int num){
if (num < 2) return false;
// 检查从2到num的平方根的整数是否能整除num
for (int i = 2; i * i <= num; i++) {
if (num % i == 0) return false; // 如果能被整除,则num不是素数
}
return true; // 如果以上都不满足,则num是素数
}
int main(){
int q;
cin>>q;
for(int i=0;i<q;i++) {
long long n, k;
cin >> n >> k;
unordered_map<long long, int> map;
int index=2;
while (n!=1) {
for (int j = index; j <= n; j++) {
if (isPrime(j) && n % j == 0) {
n /= j;
map[j]++;
index=j;
break;
}
}
if(isPrime(n)){
map[n]++;
break;
}
}
long long num=1;
for (auto it: map){
if(it.second>=k){
num*=pow(it.first,it.second);
}
}
cout<<num<<endl;
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
while(n--){
long long a, b;
int k;
cin >> a >> k;
unordered_map<int, int> map;
b = a;
for(int i = 2; i <= a && i <= b;){
if(b % i == 0){
map[i]++;
b /= i;
}else
i++;
}
for(auto it : map){
while(it.second != 0 && it.second < k){
a /= it.first;
it.second--;
}
}
cout << a << endl;
}
return 0;
}
202312-3 树上搜索
小C观察了事先收集到的数据,并加以统计,得到了一个名词属于各个类别的可能性大小的信息。具体而言,每个类别都可以赋予一个被称为权重的值,值越大,说明一个名词属于该类别的可能性越大。由于每次向用户的询问可以获得两种回答,小C联想到了二分策略。他设计的策略如下:
1.对于每一个类别,统计它和其全部后代类别的权重之和,同时统计其余全部类别的权重之和,并求二者差值的绝对值, 计为w;
2.选择w最小的类别,如果有多个,则选取编号最小的那一个,向用户询问名词是否属于该类别;
3.如果用户回答“是”,则仅保留该类别及其后代类别,否则仅保留其余类别;
4.重复步骤1,直到只剩下一个类别,此时即可确定名词的类别。
小C请你帮忙编写一个程序,来测试这个策略的有效性。你的程序首先读取到所有的类别及其上级次级关系,以及每个类别的权重。你的程序需要测试对于被归类到给定类别的名词,按照上述策略提问,向用户提出的所有问题。
#include<bits/stdc++.h>
using namespace std;
vector<vector<int>> tree;
vector<long long> wi,tmp;
set<int> no,yes;
void dfs(int index){//深度优先搜索
yes.insert(index);
for(int i:tree[index]){
if(no.find(i)==no.end()){
dfs(i);
wi[index]+=wi[i];//更新当前节点的权重,当前节点的权重等于当前节点的权重加上子节点的权重
}
}
}
int getMinIdx(int index){
int minDiffIndex = 1;
long long minDiff = LLONG_MAX;
for(int i:yes){
long long diff = abs(wi[index] - 2 * wi[i]);
if(diff < minDiff){
minDiff = diff;
minDiffIndex = i;
}
}
return minDiffIndex;
}
bool check(int minDiffIndex,int targetIndex){
if(minDiffIndex == targetIndex)
return true;
for(int i : tree[minDiffIndex]){
if(no.find(i)!=no.end())
continue;
if(check(i,targetIndex))
return true;
}
return false;
}
int main(){
int n,m,fath;
cin>>n>>m;
tree.resize(n+1);
tmp.resize(n+1);
for(int i=1;i<=n;i++){
cin>>tmp[i];
}
for(int i=2;i<=n;i++){
cin>>fath;
tree[fath].push_back(i);
}
while(m--){
int targetIndex;
cin>>targetIndex;
no.clear();
int index=1;
while(true){
wi=tmp;
yes.clear();
dfs(index);
if(yes.size()==1){
break;
}
int minDiffIndex = getMinIdx(index);
// 如果目标节点在子树中,更新当前节点;否则,将该节点添加到不可访问集合中
if (check(minDiffIndex, targetIndex)) {
index = minDiffIndex; // 如果目标节点在子树中,更新当前节点,用于下一次搜索
}
else {
no.insert(minDiffIndex); // 否则,将该节点添加到不可访问集合中
}
cout<<minDiffIndex<<" ";
}
cout<<endl;
}
return 0;
}
202312-4 宝藏
西西艾弗岛上埋藏着一份宝藏,小C根据藏宝图找到了宝藏的位置。藏有宝藏的箱子被上了锁,旁边写着一些提示:
给定n条指令,编号为1~n,其中每条指令都是对一个双端队列的操作,队列中的元素均为2x2的矩阵;
在某些时刻,某一条指令可能会改变;
在某些时刻,密码可以由以下方式计算:对于给定的指令区间[l,r],对初始为空的队列依次执行第l~r条指令,将得到的队列里的所有矩阵从头到尾相乘,并将乘积矩阵中的所有元素对 998244353取模,得到的矩阵即为密码;特别地,若队列为空,则密码为单位矩阵;如果能分别计算出这些时刻的密码,将能够打开箱子的锁,从而获得宝藏。
经过小C的观察,每条指令的形式均为以下三种之一:
1.给定2x2的矩阵A,将A插入队列的头部; 2.给定2x2的矩阵B,将B插入队列的尾部; 3.若队列非空,删除队列中最晚被插入的矩阵。
小C将所有的时刻发生的事件均记录了下来。具体地,共有m个时刻,每个时刻可能会发生两种事件:1.第i条指令改变,改变后的指令仍为以上三种形式之一;
2.给定指令区间[I,川],求依次执行第I~r条指令得到的密码。
由于小C并不会这个问题,他向你发起了求助。你需要帮助小C求出所有类型为2的事件所对应的密码。
暴力35分解
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> instruction;//记录指令
deque<vector<long long>> matrix;//记录矩阵
deque<int> st;//记录矩阵最晚插入
void Execute(int l, int r){
st.clear();
matrix.clear();
for(int i = l; i <= r; i++){
if(instruction[i][0] == 1){
st.push_front(instruction[i][0]);
matrix.push_front({instruction[i][1],instruction[i][2],instruction[i][3],instruction[i][4]});
}else if(instruction[i][0] == 2){
st.push_front(instruction[i][0]);
matrix.push_back({instruction[i][1],instruction[i][2],instruction[i][3],instruction[i][4]});
}else if(instruction[i][0] == 3 && matrix.size()){
if(st.front() == 1){
matrix.pop_front();
st.pop_front();
}else if(st.front() == 2){
matrix.pop_back();
st.pop_front();
}
}
}
if(matrix.size() == 0){ //很坑的一点,队列为空时取单位矩阵
cout << "1 0 0 1" << endl;
return;
}
while(matrix.size() > 1){
vector<long long> tmp = matrix.front();
matrix.pop_front();
long long a, b, c, d;
a = (tmp[0]*matrix[0][0])%998244353 + (tmp[1]*matrix[0][2])%998244353;
b = (tmp[0]*matrix[0][1])%998244353 + (tmp[1]*matrix[0][3])%998244353;
c = (tmp[2]*matrix[0][0])%998244353 + (tmp[3]*matrix[0][2])%998244353;
d = (tmp[2]*matrix[0][1])%998244353 + (tmp[3]*matrix[0][3])%998244353;
matrix[0][0] = a%998244353;
matrix[0][1] = b%998244353;
matrix[0][2] = c%998244353;
matrix[0][3] = d%998244353;
}
for(int i = 0;i < 4; i++)
cout << matrix[0][i]%998244353 << " ";
cout << endl;
}
int main(){
int n, m;
cin >> n >> m;
int u, v;
while(n--){
cin >> v;
if(v == 1 || v == 2){
int a, b, c, d;
cin >> a >> b >> c >> d;
instruction.push_back({v, a, b, c, d});
}else if(v == 3){
instruction.push_back({v, 0, 0, 0, 0});
}
}
while(m--){
cin >> u;
if(u == 1){
int i;
cin >> i >> v;
if(v == 1 || v == 2){
instruction[i - 1][0] = v;
cin >> instruction[i - 1][1] >> instruction[i - 1][2] >> instruction[i - 1][3] >> instruction[i - 1][4];
}else if(v == 3){
instruction[i - 1][0] = 3;
}
}else if(u == 2){
int l, r;
cin >> l >> r;
Execute(l - 1, r - 1);
}
}
return 0;
}
202312-5 彩色路径
暴力20分解
#include <bits/stdc++.h>
using namespace std;
#define N 5000
vector<int> C(100);//图中每个节点的颜色标签
vector<int> U(N);//每条有向边的起点
vector<int> V(N);//每条有向边的终点
vector<int> D(N);//每条有向边的长度
vector<pair<int, int>> G[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, l, k;
cin >> n >> m >> l >> k;
vector<vector<int>> dp(n, vector<int>(l, INT_MIN));//dis[i][j] 表示经过j条边,最终到达i号节点的最长路的长度。
dp[0][0] = 0;
for (int i = 0; i < n; i++) {
cin >> C[i];
}
for (int i = 0; i < m; i++) {
cin >> U[i];
}
for (int i = 0; i < m; i++) {
cin >> V[i];
}
for (int i = 0; i < m; i++) {
cin >> D[i];
G[U[i]].push_back({V[i], D[i]});
}
for (int u = 0; u < n - 1; u++){ //u表示起点
for (auto tem: G[u]) {
int v = tem.first, w = tem.second;//v表示终点,w表示u到v的边长
for (int i = 1; i < l; i++) {
if (dp[u][i - 1] == INT_MIN)
continue;
dp[v][i] = max(dp[v][i], dp[u][i - 1] + w);
}
}
}
sort(dp[n - 1].rbegin(), dp[n - 1].rend());
cout << dp[n - 1][0];
return 0;
}