1001 Mod, Or and Everything
问题描述
你需要给出一个整数n,需要计算(n mod 1) | (n mod 2) |… | (n mod (n - 1)) | (n mod n).
其中“|”代表位运算或
输入
第一行包含整数T(1≤T≤5000)表示测试案例的数量。
对于每个测试案例,都有一个
整数n(1≤n≤ 1012 )在一行。
输出
对于每个测试案例,将答案打印在一行中。
示例输入
5
1
2
3
4
5
样本输出
0
0
1
1
3
解题思路
签到题。公式不太好打还是直接贴图吧······
代码
#include<bits/stdc++.h>
#define ll long long
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int _; for (cin >> _; _--;) {
ll n; cin >> n;
if (n == 1)cout << "0\n";
else {
int k = log(n) / log(2);
ll sum = pow(2, k);
if (sum != n)cout << sum - 1 << "\n";
else cout << sum / 2 - 1 << "\n";
}
}
}
1005 Minimun spanning tree(最小生成数)
问题描述
给定 n-1 个点,从 2 到 n 数,A 和 b 之间的边权为 lcm(a, b)。请找到由他们形成的最小跨度树。
最小跨度树是连接的边加权非定向图边缘的子集,该图将所有顶点连接在一起,没有任何周期,并且具有尽可能小的总边缘重量。这就是说,它是一棵跨越的树,其边缘重量的总和尽可能小。
输入
第一行包含一个整数 t (t<=100),表示输入中的测试案例数。然后t测试案例如下。
每个测试案例的唯一行包含上述一个整数 n (2<\n<=100000000)。
输出
对于每个测试案例,将一个整数打印成一行,这是树缘重量的总和。
示例输入
2
2
6
样本输出
0
26
解题思路
考虑编号为 i, j 的两个点,根据 lcm 的性质,lcm(i, j) = i * j / gcd(i, j) 。
对于编号为3~n的点,将所有编号为合数的点向其约数连边,编号为质数的点向2连边,不难证明这样形成的生成树是最小的那么。
那么,对于质数 i ,它对边权和的贡献仅为 i*2,对于合数 i ,它对边权和的贡献仅为 i。所以,计算最小的边权和,需要计算 2~n 内的质数和 S1 以及合数和 S2,得出最终答案为 S1 * 2 + S2 。用线性筛,求个前缀和,我们就能够在 O(n) 的时间复杂度下解决该问题。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 7;
int pri[N], cnt;
ll ans[N];
bool ispri[N];
void prim()//线性筛法
{
for (int i = 2; i < N; i++)
ispri[i] = 1;
for (int i = 2; i < N; i++)
{
if (ispri[i])
pri[++cnt] = i;//先全部标记为素数
for (int j = 1; j <= cnt; j++)
{
if (i * pri[j] >= N)
break;
ispri[i * pri[j]] = 0;//标记为合数
if (i % pri[j] == 0)
break;
}
}
}
int main()
{
int t,n[100];
scanf("%d",&t);
for(int i=0;i<t;i++)
{
scanf("%d",&n[i]);
}
prim();
for(int i=0;i<t;i++)
{
for (int j = 3; j < n[i]; j++) // 类比前缀和求解答案数组
{
if (ispri[j])
ans[j] = ans[j - 1] + j * 2;
else
ans[j] = ans[j - 1] + j;
}
printf("%lld",ans[i])
}
return 0;
}
补充: 线性筛法
若 x 是素数,它的倍数是合数;而若 x 是合数,那么它的倍数也一定是合数;
因此,如果我们从小到大考虑每个数,然后把当前这个数的所有(比自己大的)倍数记为合数,那么运行时没有被标记的数就是素数了
实际运行中会有合数被标记了多次,如果我们保证每个数只会被标记一次,即每个数我们仅在枚举到它的最小素因子时进行标记,那么我们就可以显著地优化筛法的时间复杂度。
1006 Xor sum (Good ,字典树)
问题描述
给定长度 n 的整数序列,找到最短的连续子序列使得异或和不低于 k。
如果存在多个满足条件的序列,请输出左端点最小的连续子序列的左端点和右端点,
如果不存在连续子序列的异或和 不小于 k 则输出 -1
输入
第一行包含一个整数 t (t<=100),表示输入中的测试案例数。然后t测试案例如下。
每个测试案例的第一行包含两个整数 n(1<=n<=100000) 和 k (0<=k<2^30),表示序列的
长度。
每个测试的第二行包含 n 整数 ai (0<=ai<2^30),按顺序表示整
数。
测试 异或和n >1000 的数量不超过 5。
输出
对于每个测试案例,将两个整数打印在一行中,表示连续子句数的左端点和右端点。
如果没有连续的子序异或和不小于 k,在一行中打印"-1"。
示例输入
2
3 2
1 2 2
9 7
3 1 3 2 4 0 3 5 1
样本输出
2 2
5 7
解题思路
讲题的时候说的要点是
·(a1^a2^...^al-1)^(a1^a2^a3^...ar)=al^al+1^...^ar
·做前缀和转化成 a1,a2,...,an中两两异或大于k 中寻找距离最近的
·考虑到可能直接从1~n要多添加一项a0=0
还没肝到字典树的小白有点晕 (¦3」∠)
先存个档,具体思路可以参考一下这位大神,小白讲不清楚了…2021杭电多校第一场 (1) 个人补题记录 - RioTian (cnblogs.com)
代码
参考上面的
1008 Maxial Submatrix
问题描述
给出n行和m列的矩阵,找到每个列上未减少的最大区域子矩阵
输入
第一行包含整数T(1≤T≤10)表示测试案例的数量。
对于每个测试案例,第一行包含两个整数n,m(1≤n,m≤2∗ 103)表示下一行所遵循
的下一行矩阵的大小。i-th行包含m整数vij(1≤vij≤5∗103)表示矩阵
的价值保证不超过2个试剂箱n∗米>10000
输出
对于每个测试案例,打印一个代表最大子矩阵的整数
示例输入
1
2 3
1 2 4
2 3 3
样本输出
4
解题思路
首先预处理出自该位置向上、满足性质的数的个数,记为 h[i][j]
对于每行 (h[i]) 维护一个单调递增栈,所有元素进栈和出栈一次,每个元素出栈时更新最大的矩形面积。
为了能够更新最大的矩形面积,我们需要在栈内记录两个数据——元素的高度 h[i][j]
和它到栈中上一个元素的宽度差。
元素出栈时,因为高度逐渐递减,故记width 为当前已经弹出的元素的宽度和
代码
const int N = 2010;
int c[N][N];
int n, m, h[N][N];
LL work(int h[N], int n) {
int l[N], r[N], q[N];
h[0] = h[n + 1] = -1;
int tt = 0;
q[0] = 0;
for (int i = 1; i <= n; i++) {
while (h[i] <= h[q[tt]]) tt--;
l[i] = q[tt];
q[++tt] = i;
}
tt = 0;
q[0] = n + 1;
for (int i = n; i >= 1; i--) {
while (h[i] <= h[q[tt]]) tt--;
r[i] = q[tt];
q[++tt] = i;
}
LL ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, (LL)h[i] * (r[i] - l[i] - 1));
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
memset(c, 0, sizeof(c));
memset(h, 0, sizeof(h));
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> c[i][j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (c[i][j] >= c[i - 1][j]) h[i][j] = h[i - 1][j] + 1;
else h[i][j] = 1;
LL ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, work(h[i], m));
}
cout << ans << endl;
}
return 0;
}
补充:单调栈
什么是单调栈?
就是维护一个栈内元素自底向上都保持单调(递增/递减)的栈。
怎么维护呢?这个要分两种情况,以自底向上单调递减的单调栈为例:
如果待入栈元素比栈顶元素小,则直接入栈
如果待入栈元素比栈顶元素大,则将栈中所有比其小的元素全部弹出,然后使待入栈元素入栈常见应用:求柱状图中的最大面积/离线RMQ问题/优化DP
[1009 KD-Graph](Problem - 1009 (hdu.edu.cn))
问题描述
让我们调用加权连接的非定向图形n如果满足
以下条件,则边缘和m边缘KD-图形:
* n眩晕被严格分为K组,每个组包含至少一个顶点
*如果顶点p和问 ( p≠问)在同一组,必须至少有一个路径之间p和问在此路径中满足最大值小于或等于D.
p和问 ( p≠问)在不同的组,不能有任何路径之间p和问在此路径中满足最大值小于或等于D.
您将获得一个加权连接的非定
向图形G之n紫杉和m边缘和整数K.
您的任务是找到最小非
负数D这可以使有一种方法来划分n眩晕成K组使G满足KD图的定义。−1如果没有这样的D存在。
输入
第一行包含整数T(1≤T≤5)代表测试案例的数量。
对于每个测试案例,有三个整数n,m,k(2≤n≤100000,1≤米≤500000,1≤k≤n )在第一行。
下一个m行包含三个整数u,v和c (1≤v,u≤n,v≠u,1≤c≤109)这意味着在紫杉之间有一个边缘美国和v重量c.
输出
对于每个测试案例,在新行中打印一个整数。
示例输入
2
3 2 2
1 2 3
2 3 5
3 2 2
1 2 3
2 3 3
样本输出
3
-1
解题思路
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1e6+10;
int T,n,m,k,fa[maxn],now,ans;
struct da{int u,v,w;}q[maxn];
bool cmp(da aa,da bb){return aa.w<bb.w;}
int getf(int x)
{
if (fa[x]==x) return x;
fa[x]=getf(fa[x]);
return fa[x];
}
void solve()
{
scanf("%d%d%d",&n,&m,&k); now=n; ans=0;
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=m;i++) scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w);
sort(q+1,q+m+1,cmp);
for (int i=1;i<=m;i++)
{
if (q[i].w!=q[i-1].w){if (now==k) {printf("%d\n",ans); return;}}
if (getf(q[i].u)==getf(q[i].v)) continue;
now--; fa[getf(q[i].u)]=getf(q[i].v); ans=q[i].w;
}
printf("%d\n",now==k?ans:-1);
}
int main()
{
scanf("%d",&T);
while (T--) solve();
return 0;
}