困于心,衡于虑,而后作。
打卡第四篇。
A.求和
B.工作时长
#include <bits/stdc++.h>
using namespace std;
//2022是平年
int Month[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
int sum[520];
int month, day, h, mi, s; //月,日,时,分,秒
int main() {
int ans = 0;
for (int i = 0; i < 520; i++) {
string str;
getline(cin, str);
month = (str[6] - '0') + (str[5] - '0') * 10;
day = (str[9] - '0') + (str[8] - '0') * 10;
h = (str[12] - '0') + (str[11] - '0') * 10;
mi = (str[15] - '0') + (str[14] - '0') * 10;
s = (str[18] - '0') + (str[17] - '0') * 10;
sum[i] = (Month[month - 1] + day) * 86400 + h * 3600 + mi * 60 + s;
}
sort(sum, sum + 520);
for (int i = 0; i < 520; i += 2) {
ans += sum[i + 1] - sum[i];
}
cout << ans << endl;
return 0;
}
C.三国游戏
真服了,调试半天没过,原来是没看清输入,for(l i=0;i<n;i++)cin>>x[i]>>y[i]>>z[i];无语了......
正确代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll work(vector<ll> &a,vector<ll> &b,vector<ll> &c,ll &n){
vector<ll> w(n,0);
for(ll i=0;i<n;i++){
w[i]=(a[i]-b[i]-c[i]);
}
sort(w.rbegin(),w.rend());
//for(ll i=0;i<n;i++)cout<<w[i]<<endl;
//if(w[0]<=0)return -1;
ll flag=-1,res=0;
for(ll i=0;i<n;i++){
res+=w[i];
if(res>0){
flag=i+1;
}
else break;
}
return flag;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll n;cin>>n;
vector<ll> x(n),y(n),z(n);
for(int i = 0; i < n; i++) cin >> x[i];
for(int i = 0; i < n; i++) cin >> y[i];
for(int i = 0; i < n; i++) cin >> z[i];
ll MAX=max({work(x,y,z,n),work(z,y,x,n),work(y,x,z,n)});
cout<<MAX;
return 0;
}
D.填充
错误代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
string s;cin>>s;
//cout<<s<<endl;
for(ll i=0;i<s.length()-1;i++){
if(s[i]=='?'){
if(i==0){
if(s[1]=='0')s[i]='0';
else if(s[1]=='1')s[i]='1';
else if(s[1]=='?'){
//?? 0001
//00 0001
//11 0001
//10 0001
//01 0001
s[0]='0';s[1]='0';
//s[0]='1';s[1]='1';
}
}else{
if(s[i+1]=='0')s[i]='0';
else if(s[i+1]=='1')s[i]='1';
else if(s[i+1]=='?'){
//1010??11111
//10100011111
s[i]=s[i-1];
//不用担心越界,此时有大前提i>0,也别管s[i+1]=s[i+2],不然遍历范围改变又乱套了
}
}
}
}
if(s[s.length()-1]=='?'){
s[s.length()-1]=s[s.length()-2];
}
//cout<<s<<endl;
ll count=0;
ll i=1;
while(i<s.length()){
if((s[i]=='0'&&s[i-1]=='0')||(s[i]=='1'&&s[i-1]=='1')){
count++;
i+=2;
}else i++;
}
cout<<count;
return 0;
}
/*
1110000????11010101???
1110000????11010101???
1110000000111010101111
*/
打表测试发现,第7个样例还是不够严谨。。
答案代码:
其实透过问题看本质也就是,四种情况:
1. 00或者11组合,count++,跳转步长为2.
2. 10或者01组合,步长为1,跳到i+1继续遍历查询。
3. ??组合,??可以是11,也可以是00,爱是谁是谁,count++,i+=2;
4. 1?,?1, 0?, ?0,也就两个字符里面包含?,别管另一个字符到底是1还是0,因为?肯定跟着另一个字符变,爱是谁是谁,count++,i+=2;
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
string s;cin>>s;
ll count=0;
for(ll i=0;i<s.length()-1;i++){
if(s[i]==s[i+1]||s[i]=='?'||s[i+1]=='?'){
count++;
i+=1;
}else continue;
}
cout<<count;
return 0;
}
E.翻转
和填充的思路差不多。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ll n;cin>>n;
while(n--){
string t,s;cin>>t>>s;
ll i=1;
ll count=0;
while(i<t.length()-1){
if(t[i]==s[i])i++;
else{
if(i<=t.length()-2&&s[i-1]!=s[i]&&s[i]!=s[i+1]){
s[i]=t[i];
count++;
i+=2;
}
else i++;
}
}
//cout<<s<<'\n';
if(t==s)cout<<count<<'\n';
else cout<<-1<<'\n';
}
return 0;
}
F.子矩阵
#include <bits/stdc++.h>
using namespace std;
static const long long MOD = 998244353;
/*
* 一维滑窗 - 求区间最大值
* 输入: arr 原数组, k 窗口大小
* 返回: 一个 vector, 其长度为 arr.size() - k + 1
* 第 i 个元素是 arr[i..i+k-1] 的最大值
*/
vector<long long> slidingWindowMax(const vector<long long>& arr, int k) {
deque<int> dq; // 存放下标, 保证队首是当前窗口的最大值下标
vector<long long> res;
res.reserve(arr.size() - k + 1);
for (int i = 0; i < (int)arr.size(); i++) {
// 1) 保证队尾对应的 arr 值 >= 当前元素, 不符合就弹出
while (!dq.empty() && arr[dq.back()] <= arr[i]) {
dq.pop_back();
}
dq.push_back(i);
// 2) 窗口已经超过大小 k,移除队首
if (dq.front() <= i - k) {
dq.pop_front();
}
// 3) 当 i >= k-1 时,窗口 [i-k+1..i] 已形成,队首即最大值
if (i >= k - 1) {
res.push_back(arr[dq.front()]);
}
}
return res;
}
/*
* 一维滑窗 - 求区间最小值
* 方法与最大值类似,只是比较符号相反
*/
vector<long long> slidingWindowMin(const vector<long long>& arr, int k) {
deque<int> dq;
vector<long long> res;
res.reserve(arr.size() - k + 1);
for (int i = 0; i < (int)arr.size(); i++) {
// 弹出所有 >= arr[i] 的元素,以保证队首是最小值
while (!dq.empty() && arr[dq.back()] >= arr[i]) {
dq.pop_back();
}
dq.push_back(i);
// 移除不在窗口内的下标
if (dq.front() <= i - k) {
dq.pop_front();
}
// 当 i >= k-1 时,队首即窗口最小值
if (i >= k - 1) {
res.push_back(arr[dq.front()]);
}
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, a, b;
cin >> n >> m >> a >> b; // n 行 m 列, 子矩阵 a 行 b 列
// 读入矩阵
vector<vector<long long>> mat(n, vector<long long>(m));
for (int i = 0; i < n; i++){
for (int j = 0; j < m; j++){
cin >> mat[i][j];
}
}
// 第一步: 行方向滑窗, 得到每一行的区间最值 (长 m-b+1)
// maxRow[r][c]: 第 r 行,从列 c 开始长度为 b 的窗口最大值
// minRow[r][c]: 第 r 行,从列 c 开始长度为 b 的窗口最小值
vector<vector<long long>> maxRow(n), minRow(n);
for (int r = 0; r < n; r++){
maxRow[r] = slidingWindowMax(mat[r], b); // 大小 (m - b + 1)
minRow[r] = slidingWindowMin(mat[r], b);
}
// 第二步: 再在列方向进行大小为 a 的滑窗
// 我们有 n 行, 每行 (m - b + 1) 列
// 对于每一列 c (0 <= c < m-b+1),把 maxRow[r][c] (r=0..n-1) 视为一个数组
// 在它上做窗口大小 a 的一维滑窗, 结果就是最终 (n-a+1) 行
// 同理对 minRow 也一样
// 最终得到的 maxVal[i][c], minVal[i][c] 即对应子矩阵的最大/最小值
vector<vector<long long>> maxVal(n - a + 1, vector<long long>(m - b + 1));
vector<vector<long long>> minVal(n - a + 1, vector<long long>(m - b + 1));
for (int c = 0; c < m - b + 1; c++){
// 准备一个临时数组, 收集 maxRow[r][c] (r=0..n-1)
vector<long long> colArrMax(n), colArrMin(n);
for (int r = 0; r < n; r++){
colArrMax[r] = maxRow[r][c];
colArrMin[r] = minRow[r][c];
}
// 对 colArrMax / colArrMin 做大小为 a 的滑窗
vector<long long> colMaxRes = slidingWindowMax(colArrMax, a); // 大小 n-a+1
vector<long long> colMinRes = slidingWindowMin(colArrMin, a);
// 将结果写回 maxVal / minVal
for (int r = 0; r < n - a + 1; r++){
maxVal[r][c] = colMaxRes[r];
minVal[r][c] = colMinRes[r];
}
}
// 第三步: 计算所有子矩阵 (a x b) 的价值之和 (max * min),并取模
long long ans = 0;
for (int i = 0; i < n - a + 1; i++){
for (int j = 0; j < m - b + 1; j++){
// 可能达到 1e9 * 1e9 = 1e18, 在 long long 范围内
long long val = (maxVal[i][j] % MOD) * (minVal[i][j] % MOD) % MOD;
ans = (ans + val) % MOD;
}
}
cout << ans << "\n";
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
struct point{
ll val;
int x;
int y;
bool operator > (point t) const{
return val >= t.val;
}
bool operator < (point t) const{
return val <= t.val;
}
};
struct num{
int maxn;
int minn;
};
const int N = 1010;
ll f[N][N];
num nums[N][N];
int n,m,a,b;
void init(){
// 对于每一列建立两个优先队列:
// Q[i] 用来维护最小值(采用 min-heap,即使用 greater<point>)
// M[i] 用来维护最大值(采用 max-heap,即使用 less<point>)
priority_queue<point, vector<point>, greater<point>> Q[N];
priority_queue<point, vector<point>, less<point>> M[N];
// 第一部分:初始化每一列的第一个滑动窗口 [1, a]
for(int i=1;i<=m;i++){
for(int j=1;j<=a;j++){
Q[i].push({f[j][i], i, j});
M[i].push({f[j][i], i, j});
}
// 记录第一个窗口的最小值和最大值
nums[1][i].maxn = M[i].top().val;
nums[1][i].minn = Q[i].top().val;
}
// 第二部分:向下滑动窗口,每次增加一行,去掉窗口外的数
for(int i=1;i<=m;i++){
for(int j=2;j<=n-a+1;j++){
// 将新的进入窗口的元素加入队列,位置为 (j+a-1, i)
Q[i].push({f[j+a-1][i], i, j+a-1});
M[i].push({f[j+a-1][i], i, j+a-1});
// 若队列顶端的元素不在当前窗口内,则弹出
while(Q[i].top().y < j){
Q[i].pop();
}
while(M[i].top().y < j){
M[i].pop();
}
// 更新当前窗口对应的最小值和最大值
nums[j][i].maxn = M[i].top().val;
nums[j][i].minn = Q[i].top().val;
}
}
}
int main(){
ll res = 0;
// 用于水平滑动窗口的两个优先队列:
// Q 用来维护最小值,采用 min-heap (greater<point>)
// M 用来维护最大值,采用 max-heap (less<point>)
priority_queue<point, vector<point>, greater<point>> Q;
priority_queue<point, vector<point>, less<point>> M;
cin >> n >> m >> a >> b;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin >> f[i][j];
}
}
// 预处理:先在垂直方向求出每列的 a 行区间最值
init();
// 对于每个垂直窗口(行窗口),即从第 i 行开始的 a 行
for(int i = 1; i <= n-a+1; i++){
// 初始化水平方向的第一个窗口:前 b 列
for(int j=1; j<=b; j++){
Q.push({nums[i][j].minn, j, i});
M.push({nums[i][j].maxn, j, i});
}
// 当前水平窗口对应的子矩阵的最小值为 Q.top().val,最大值为 M.top().val
res += (Q.top().val * M.top().val) % mod;
// 向右滑动窗口,处理剩余窗口
for(int j=2; j<=m-b+1; j++){
// 将新进入窗口的那一列加入队列
Q.push({nums[i][j+b-1].minn, j+b-1, i});
M.push({nums[i][j+b-1].maxn, j+b-1, i});
// 弹出那些已经不在当前窗口内的元素(依据存入时记录的列号 x)
while(Q.top().x < j){
Q.pop();
}
while(M.top().x < j){
M.pop();
}
// 累加当前窗口的贡献
res += (Q.top().val * M.top().val) % mod;
}
// 清空两个优先队列,为下一个行窗口作准备
while(!Q.empty()){
Q.pop();
}
while(!M.empty()){
M.pop();
}
}
cout << res % mod << endl;
return 0;
}
G.互质数的个数 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=998244353;
ll qsm(ll a,ll b){
ll ans=1;
while(b){
if(b&1)ans=(ans%N*a%N)%N;
a=(a%N*a%N)%N;
b>>=1;
}
return ans;
}
ll Euler(ll ans){
ll res=ans;
for(ll i=2;i<=ans/i;i++){
if(ans%i==0){
res=(res%N/i%N*(i-1))%N;
while(ans%i==0)ans/=i;
}
}
if(ans>1){
res=res%N/ans%N*(ans-1)%N;
}
return (res+N)%N;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll a,b;cin>>a>>b;
ll ans=qsm(a,b);
ll res=Euler(ans)%N;
cout<<res;
return 0;
}
H.异或和之差
字典树。。以后学树专题了再来补题。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std ;
const int N = 1e6 + 10 ;
int n , a[N] ;
int son[2][N] , idx ;
int mx[N] , mi[N] ;
void add(int x)
{
int p = 0 ;
for(int i = 20 ; i >= 0 ; i --)
{
int u = (x>>i) & 1; //判断x的第i位上是1/0
if(!son[u][p]) son[u][p] = ++idx; //若节点不存在,则按照当前位置上的数是多少来构造树
p = son[u][p]; //指向构造好的节点准备构造下一位
}
}
int query_mx(int x)
{//异或值最大则需要尽可能多【不相同】的位数
int p = 0 , res = 0;
for(int i = 20;i >= 0;i --)
{
int u = (x >> i) & 1;
if(son[!u][p]) res |= (1 << i);
else u = !u;
p = son[!u][p];
}
return res;
}
int query_mi(int x)
{//异或值最小则需要尽可能多【相同】的位数
int p = 0 , res = 0;
for(int i = 20;i >= 0;i --)
{
int u = (x >> i) & 1;
if(!son[u][p]) //若与当前位相同的节点不存在,则最小异或
{
u = !u;
res |= (1 << i);
}
p = son[u][p];
}
return res;
}
int main()
{
cin>>n;
for(int i = 1;i <= n;i ++) cin>>a[i];
mx[0] = 0 , mi[0] = 1e9;
int sum = 0;
add(sum);
for(int i = 1;i <= n;i ++)
{//寻找i左边的异或和最大值和最小值
sum ^= a[i]; //前缀和
mx[i] = max(mx[i-1] , query_mx(sum)); //动态规划,i左边的最大值=i-1左边的最大值(不包括i)和以i为右端点(包括i)的最大值
mi[i] = min(mi[i-1] , query_mi(sum));
add(sum); //构造树
}
memset(son , 0 , sizeof son) ;
idx = 0 ; sum = 0 ;
int ans = 0 , mx2 = 0 , mi2 = 2e9;
add(sum) ;
for(int i = n ; i ; i --)
{//寻找i右边的异或和最大值和最小值(倒序)
sum ^= a[i] ;
mx2 = max(mx2 , query_mx(sum)) ; //同上
mi2 = min(mi2 , query_mi(sum)) ;
//取(ans , 右最大-左最小 , 左最大-右最小)的最大值
ans = max({ans , mx[i - 1] - mi2 , mx2 - mi[i - 1]}) ;
add(sum) ;
}
cout<<ans<<endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
// -----------------------------------------------------
// 位Trie,用于维护一批整数的二进制表示,支持:
// insert(x) :向Trie插入一个数 x
// queryMax(x):在Trie现有数的集合中,与 x XOR 最大的值
// queryMin(x):在Trie现有数的集合中,与 x XOR 最小的值
// 题目给定 Ai ≤ 2^20,因此我们只需 21 位 (0~20)。
// -----------------------------------------------------
static const int BIT_SIZE = 21; // 针对 0 <= A[i] <= 2^20
struct XORTrie {
// trie[u][0 or 1] 存储节点 u 的儿子在位 0 / 1 的索引, -1 表示无
vector<array<int,2>> trie;
// 记录该节点下有多少前缀(可不需要计数也行, 但有时要判空)
// 构造时给定一个比较大的初始容量
XORTrie() {
trie.push_back({-1, -1});
}
// 向 Trie 中插入一个数 x
void insert(unsigned x) {
int u = 0; // 根
for(int b = BIT_SIZE - 1; b >= 0; b--){
// 取到 x 在第 b 位的值
int bit = (x >> b) & 1;
// 若没有这条边,就创建
if(trie[u][bit] < 0){
trie[u][bit] = trie.size();
trie.push_back({-1, -1});
}
u = trie[u][bit];
}
}
// 在 Trie 中查找与 x 做 XOR 后能得到的最大值
unsigned queryMax(unsigned x){
int u = 0;
unsigned ret = 0;
for(int b = BIT_SIZE - 1; b >= 0; b--){
int bit = (x >> b) & 1;
// 贪心:如果能走到与 bit 相反的分支,会得到更大异或
int go = bit ^ 1;
if(trie[u][go] < 0) {
go = bit; // 只能走相同的
}
u = trie[u][go];
// go != bit说明我们得到了更高位的 1
ret = (ret << 1) | (go ^ bit);
}
return ret;
}
// 在 Trie 中查找与 x 做 XOR 后能得到的最小值
unsigned queryMin(unsigned x){
int u = 0;
unsigned ret = 0;
for(int b = BIT_SIZE - 1; b >= 0; b--){
int bit = (x >> b) & 1;
// 贪心:如果能走到和 bit 相同的分支,会得到更小异或
int go = bit;
if(trie[u][go] < 0) {
go = bit ^ 1; // 不得不走相反的
}
u = trie[u][go];
ret = (ret << 1) | (go ^ bit);
}
return ret;
}
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<unsigned> A(n+1);
for(int i=1; i<=n; i++){
cin >> A[i];
}
// ------------------------
// 1) 从左到右:prefix[i] = A[1] XOR A[2] XOR ... XOR A[i]
// 求出在 [1..i] 上能取到的子段异或的最大值 & 最小值
// ------------------------
vector<unsigned> prefix(n+1, 0U);
for(int i = 1; i <= n; i++){
prefix[i] = prefix[i-1] ^ A[i];
}
// leftMax[i]:在区间 [1..i] 上可取到的子段异或最大值
// leftMin[i]:在区间 [1..i] 上可取到的子段异或最小值
vector<unsigned> leftMax(n+1), leftMin(n+1);
// 初始化
leftMax[0] = 0; // [1..0]无效,可当作 0
leftMin[0] = 0; // 同理
XORTrie trieMaxL, trieMinL;
trieMaxL.insert(0); // 插入 prefix[0] = 0
trieMinL.insert(0);
unsigned curMax = 0, curMin = 0; // 记录当前能取到的最大/最小子段异或
for(int i = 1; i <= n; i++){
// 以 prefix[i] 结尾的子段 XOR 可能达到的极大值
unsigned bestMax = trieMaxL.queryMax(prefix[i]);
curMax = max(curMax, bestMax);
// 以 prefix[i] 结尾的子段 XOR 可能达到的极小值
unsigned bestMin = trieMinL.queryMin(prefix[i]);
// 因为一开始是0,先赋值一下
if(i==1) curMin = bestMin;
else curMin = min(curMin, bestMin);
leftMax[i] = max(leftMax[i-1], curMax);
leftMin[i] = (i==1 ? bestMin : min(leftMin[i-1], curMin));
// 将 prefix[i] 插入 Trie
trieMaxL.insert(prefix[i]);
trieMinL.insert(prefix[i]);
}
// ------------------------
// 2) 从右到左:suffix[i] = A[i] XOR A[i+1] XOR ... XOR A[n]
// 求出在 [i..n] 上能取到的子段异或最大值 & 最小值
// ------------------------
vector<unsigned> suffix(n+2, 0U);
// suffix[n+1]=0
for(int i = n; i >= 1; i--){
suffix[i] = suffix[i+1] ^ A[i];
}
vector<unsigned> rightMax(n+2), rightMin(n+2);
XORTrie trieMaxR, trieMinR;
trieMaxR.insert(0);
trieMinR.insert(0);
unsigned curMaxR=0, curMinR=0;
// 这里从 n 往 1 遍历
for(int i=n; i>=1; i--){
// 以 suffix[i] 结尾(从右数)的子段异或极大值
unsigned bestMax = trieMaxR.queryMax(suffix[i]);
curMaxR = max(curMaxR, bestMax);
// 以 suffix[i] 结尾(从右数)的子段异或极小值
unsigned bestMin = trieMinR.queryMin(suffix[i]);
if(i==n) curMinR = bestMin;
else curMinR = min(curMinR, bestMin);
rightMax[i] = (i==n ? bestMax : max(rightMax[i+1], curMaxR));
rightMin[i] = (i==n ? bestMin : min(rightMin[i+1], curMinR));
// 插入
trieMaxR.insert(suffix[i]);
trieMinR.insert(suffix[i]);
}
// ------------------------
// 3) 枚举分割点 i,左右子段分别落在 [1..i] 和 [i+1..n]
// 差值为:
// leftMax[i] - rightMin[i+1]
// 或
// rightMax[i+1] - leftMin[i]
// ------------------------
long long ans = LLONG_MIN;
// 用 long long 避免大数减法错误
for(int i=1; i < n; i++){
// 两种方案
long long cand1 = (long long)leftMax[i] - (long long)rightMin[i+1];
long long cand2 = (long long)rightMax[i+1] - (long long)leftMin[i];
ans = max(ans, max(cand1, cand2));
}
cout << ans << "\n";
return 0;
}
I.公因数匹配
暴力:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll n;cin>>n;
vector<ll> a(n+1,0);
for(ll i=1;i<=n;i++)cin>>a[i];
for(ll i=1;i<=n;i++){
for(ll j=i+1;j<=n;j++){
if(__gcd(a[i],a[j])>1){
cout<<i<<" "<<j;
return 0;
}
}
}
return 0;
}
答案:
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
// 用 unordered_map 存储因子 -> 该因子第一次出现的位置(1-based)
unordered_map<int,int> mp;
int minIndex = 99999999, maxIndex = 0; // 最终答案,初值和 Java 代码一致
int r = minIndex, l = 0; // 用于记录临时的较早位置 r 与当前位置 l
for (int i = 0; i < n; i++){
int a;
cin >> a;
// k 取 sqrt(a)+2,以防出现精度问题或遗漏因子
int k = (int)(sqrt(a) + 2);
// 枚举因子 j,从 2 到 k
for (int j = 2; j <= k; j++){
if (a % j == 0 && a != j) { // 如果 j 是 a 的因子,并且 a != j
// 处理因子 j
if (mp.find(j) == mp.end()){
mp[j] = i + 1; // 若 j 不存在,则存入当前下标 (1-based)
} else if (mp[j] != i + 1){
r = min(r, mp[j]);
l = i + 1;
}
// 同时 a/j 也是 a 的一个因子
int factor = a / j;
if (mp.find(factor) == mp.end()){
mp[factor] = i + 1;
} else if (mp[factor] != i + 1){
r = min(r, mp[factor]);
l = i + 1;
}
}
}
// 处理 a 本身(因为一个数的因子中也包括它自身)
if (mp.find(a) == mp.end()){
mp[a] = i + 1;
} else if (a != 1 && mp[a] != i + 1){
r = min(r, mp[a]);
l = i + 1;
}
// 如果本次出现有更新,则记录答案
if (r != minIndex) {
minIndex = r;
maxIndex = l;
}
}
cout << minIndex << " " << maxIndex << "\n";
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+1;
//埃拉托色尼筛法筛选出前N个数中的所有素数,方便提高自因数分解的效率
vector<ll> primes;
void sieve(){
vector<bool> is_prime(N,true);
is_prime[0]=is_prime[1]=false;
for(ll i=2;i*i<N;i++){
if(is_prime[i]){
for(ll j=i*i;j<N;j+=i){
is_prime[j]=false;
}
}
}
for(ll i=2;i<N;i++){
if(is_prime[i]){
primes.push_back(i);
}
}
}
void fenjie(ll x,vector<ll> &fac){
//如果是素数,其因子只有1和他本身,不存1,不然有影响
if(find(primes.begin(),primes.end(),x)!=primes.end()){
fac.push_back(x);
}else{
ll cur=x;
for(ll i=2;i<=cur/i;i++){
if(cur%i==0){
fac.push_back(i);
while(cur%i==0)cur/=i;
}
}
if(cur>1)fac.push_back(cur);
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll n;cin>>n;
vector<ll> a(n+1,0);//存输入的数
vector<vector<ll>> mp(n+1);//存每个数,还有这个数的所有除了1的质因子
for(ll i=1;i<=n;i++){
cin>>a[i];
fenjie(a[i],mp[i]);
}
sieve();
vector<ll> pos(N,0);//存数字第一次出现的位置,
ll best_i=n+1,best_j=n+1;
for(ll i=1;i<=n;i++){
for(ll j=0;j<mp[i].size();j++){
ll p=mp[i][j];
if(pos[p]!=0){
ll yuxuan_i=pos[p];//预选i的位置
ll yuxuan_j=i;
//这个条件实现了对候选对 (candidate_i, candidate_j) 和当前最佳对 (best_i, best_j) 的字典序比较,保证最终选择 i 最小,如果 i 相同,再选 j 最小的那组
if(yuxuan_i<best_i||(yuxuan_i==best_i&&yuxuan_j<best_j)){
best_i=yuxuan_i;
best_j=yuxuan_j;
}
}else{
pos[p]=i;
}
}
}
if(best_i==n+1){
cout<<"-1 -1\n";
}else{
cout<<best_i<<" "<<best_j<<'\n';
}
return 0;
}
#include <bits/stdc++.h>
#define rep(a,b,c) for (int a = (b) ; a < (c) ; ++ a)
using namespace std ;
using ll = long long ;
using pii = pair<int,int> ;
const int maxn = 1e6 + 10 ;
int prime[maxn] ;
bitset<maxn> vis ;
vector<int> vec[maxn] ;
int a[maxn] ;
int pos[maxn] ;
int main(){
ios::sync_with_stdio(false) ;
cin.tie(0);
cout.tie(0) ;
int n ;
cin >> n ;
vis[1] = 1 ;
rep(i,2,maxn){
if (!vis[i]) prime[++ prime[0]] = i;
for (int j = 1 ; j <= prime[0] && prime[j] * i < maxn ; ++ j){
vis[prime[j] * i] = 1;
if (i % prime[j] == 0) break ;
}
}
for (int i = 1 ; i <= prime[0] ; ++ i)
for (int j = 1 ; j * prime[i] < maxn ; ++ j)
vec[prime[i] * j].push_back(prime[i]) ;
int t ;
vector<pii> res ;
rep(i,1,n + 1){
cin >> a[i] ;
int last = n + 1;
for (auto &t : vec[a[i]]){
if (pos[t])
last = min(pos[t],last) ;
else
pos[t] = i ;
}
res.push_back({last,i}) ;
}
sort(res.begin(),res.end(),[](const pii &a,const pii &b){
return a.first == b.first ? a.second < b.second : a.first < b.first ;
}) ;
cout << res[0].first << ' ' << res[0].second << endl ;
return 0 ;
}
J.子树的大小
题目不难,理清计算思路很好写。
图上在找falg时,应该是nodes[i]<=k&&nodes[i+1]>k,写错了。
调了两个小时了要,先放放不想调了 。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e9+1;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;
while(t--){
ll n,m,k;cin>>n>>m>>k;
//二叉树一个端点
vector<ll> nodes(61,1);// 2^60>1e18
//存每层的节点数
ll ans=1;
for(ll i=0;i<=61;i++){
if(nodes[i]>=N)break;
nodes[i]+=ans;//到底记录每层的结点数更方便,还是前缀和,要和下文统一
ans*=m;
}
//for(ll i=0;i<nodes.size();i++)cout<<nodes[i]<<" ";
//flag记录要找的节点K位于第几层
ll flag=0;
for(ll i=0;i<=60;i++){
if(k<nodes[i]){
flag=(i-1)+1;//位于i的前一层,下标从1开始所以要加1
break;
}
}
//cout<<flag<<'\n';
//求树高
ll shugao=1;
for(ll i=0;i<=60;i++){
if(n<nodes[i]){
shugao=i;
break;
}
}
//cout<<shugao<<'\n';
/*
n_th:最高的那层的总结点数
jiange:把k作为根节点,k对应的满子树的最高层应该有的节点数。
*/
ll n_th=0;
ll sum=0;
for(ll i=0;i<shugao-1;i++)sum+=nodes[i];
n_th=n-sum;
//cout<<n_th;
ll jiange=(ll)pow(m,(shugao-flag));
//cout<<jiange;
ll x=(n_th%jiange==0)? (n_th/jiange):(n_th/jiange+1);
//cout<<x;
ll k_th=0;
ll st=1;
for(ll i=1;i<=flag-1;i++){
st+=(ll)pow(m,(i-1));
}
k_th=(k-st)+1;
//cout<<st<<" "<<k_th;
ll sum1=0;
if(k_th<x){
sum1=(pow(m,shugao-flag+1)-1)/(m-1);
}else{
sum1=(pow(m,shugao-flag)-1)/(m-1)+(n_th-jiange*(x-1));
}
cout<<sum1<<'\n';
}
return 0;
}
答案代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;
while(t--){
ll n,m,k;cin>>n>>m>>k;
ll res=0;//记录最终结果
ll ans=1;//每层为满时的层节点数
ll l=k,r=k;
while(1){
res+=ans;
if(r*m+1>n){//第n个节点在最后一层最右边节点的左边
if((l-1)*m+2<=n){//最左边节点,第n个节点,最右边节点,在同层
res+=(n-((l-1)*m+2)+1);//坐标相减+1
}
break;
}
l=(l-1)*m+2;//更新
r=r*m+1;
ans*=m;
}
cout<<res<<'\n';
}
return 0;
}
#include <iostream>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while(T--){
long long n, m, k;
cin >> n >> m >> k;
long long ans = 0;
// 初始化左右下标和本层子树的节点数
long long l = k, r = k, res = 1;
while (true) {
ans += res; // 累加本层的节点数
// 判断下一层是否可以完全构成
if (r * m + 1 > n) {
// 下一层不满,计算剩余部分能有多少节点
if ((l - 1) * m + 2 <= n) {
ans += (n - ((l - 1) * m + 2) + 1);
}
break;
}
// 下一层是满的,则继续往下
res *= m;
r = r * m + 1;
l = (l - 1) * m + 2;
}
cout << ans << "\n";
}
return 0;
}
end.........