2021"MINIEYE杯"中超(1)补题
作者:互联网
2021"MINIEYE杯"中超(1)
1.1001 Mod, Or and Everything
考点:二进制
思路:分析两个例子:
n = 8, 那么有8 mod 8 == 0,8 mod 7 == 1,8 mod 6 == 2,8 mod 5 == 3,8 mod 4 == 0
之后再往下mod的话,所有值都是已经出现过的了,也就是说如果样例是8的话,那么答案那就是0|1|2|3的结果。
n = 9, 如上例分析,最后结果应该为0|1|2|3|4的结果。
综上我们可以知道如果输入为n,那么答案应该是0|1|...|(n - 1) / 2。接着想一下|运算,即有1出1,而且因为|运算的各个值是从小到大来的,所以只需考虑最大值的位数即可,如n=9时,最大|运算的数是4即(100),那么前两位都可以取到1,因为有4说明有3,2,1也参与了运算。
结论:只需考虑参加或运算的数中的最大值,统计max的位数,结果就是1 << 位数 - 1
不开long long见神仙。
#include <iostream>
using namespace std;
typedef long long LL;
int t;
LL x;
LL cal(LL u)//统计位数
{
int cnt = 0;
while (u)
{
cnt ++ ;
u >>= 1;
}
return cnt;
}
int main()
{
cin >> t;
while (t -- )
{
cin >> x;
cout << (1ll << cal((x - 1) / 2)) - 1 << endl;
}
return 0;
}
2.1005 Minimum spanning tree
看数据范围,不可能是prim / kruskal,就想着找规律。
边权为lcm(a, b)即a * b / gcd(a, b)。点从2开始,每个质数最佳情况是连2,边权为2 * prime,合数只需连接自己的任意一个质因子即可,边权值与合数相等。所以ans = 质数 * 2 + 合数
提交的时候记得选G++, 选C++会tle....
做法:线性筛
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10000000;
typedef long long LL;
int t;
bool st[N + 5];
int primes[N + 5], cnt;
LL n;
int main()
{
for (int i = 2; i <= N; i ++ )//线性筛
{
if (!st[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] <= N / i; j ++ )
{
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
cin >> t;
while (t -- )
{
cin >> n;
LL ans = 0;
for (LL i = 3; i <= n; i ++ )
if (!st[i]) ans += i * 2;
else ans += i;
cout << ans << endl;
}
return 0;
}
3.1006 Xor sum
看到区间异或,就想到异或前缀和 + trie树,但是用的不熟练,比赛没做出来。
思路:把题面转化成求s[l - 1] ^ s[r] >= k的最短距离,我们枚举右边终点,左边s[0] ~ s[i - 1]都是已经插入到trie树上的,之所以s[0] = 0也在树中是因为可能存在一种情况是整个序列异或在一起,所以我们要插入0来保证这种情况取得到。对于k,我们可以看它的二进制表示,从高位开始,假如某一位k的二进制是1,那么此时异或结果一定要是1才可以,如果k本位是0,那么如果此时异或结果可以是1,那么就说明有一个合法方案,取此方案。
要用pos来储存每个点在序列中的位置。每次初始化放在插入时,否则会超时。
code
#include <bits/stdc++.h>
using namespace std;
const int N = 3200010, M = 1e5 + 10;
int t;
int son[N][2], idx;//trie树
int pos[N];//每个结点对应的序列下标
int n, k;
int s[M];//异或前缀和
void solve()
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &s[i]);
s[i] ^= s[i - 1];
}
int l = -1, r = n;//存答案
int idx = 1;//trie起点
son[1][0] = son[1][1] = 0;//初始化
pos[1] = -1;
for (int i = 0; i <= n; i ++ )//依次询问s[i]并插入s[i]
{
int p = 1;
int ans = -1;
for (int j = 31; j >= 0; j -- )
{
int u = (s[i] >> j) & 1;
if ((k >> j) & 1)//k本位为1
p = son[p][u ^ 1];//只能走这里,如果没路,就break掉
else
{
if (son[p][u ^ 1])//如果异或结果是1
ans = max(ans, pos[son[p][u ^ 1]]);//记录靠右的左边界
p = son[p][u];//另一个分支记录过答案了,不需要进入那个分支
}
if (!p) break;
}
if (p) ans = max(ans, pos[p]);
if (ans >= 0 && (i - ans < r - l)) r = i, l = ans;//如果记录到了新方案,就取更优
p = 1;//插入操作
for (int j = 31; j >= 0; j -- )
{
int u = (s[i] >> j) & 1;
if (!son[p][u])//如果没有这个分支
{
son[p][u] = ++ idx;//插入字典树
son[idx][0] = son[idx][1] = 0;
pos[idx] = -1;
}
p = son[p][u];
pos[p] = max(pos[p], i);
}
}
if (l >= 0) printf("%d %d\n", l + 1, r);
else printf("-1\n");
}
int main()
{
scanf("%d", &t);
while (t -- ) solve();
return 0;
}
4.1008 Maximal submatrix
前置知识:直方图中最大的矩形(知识点涉及:单调栈)
各矩形高分别为:2 1 4 5 1 3 3,那么我们从h[1]开始,假设矩形高为h[i],向左右扩展直至扩展不了,蓝色框住的部分即为从h[1]扩展的情况,绿色部分为h[2]扩展的情况,依此类推。依次扩展结束后,我们将所得矩形面积取一个max即为答案。
换句话说,对于每个h[i],我们要找到它左边第一个比它低的位置,找到右边第一个比它低的位置,然后所得到的这个范围是L,那么此次得到的矩形面积即为L * h[i]。那么每次求左边(右边)第一个比它低的位置我们可以用单调栈来维护。假设i < j,且h[i] >= h[j],那么h[k] (k > j)寻找左边第一个比它小的高度时一定访问不到h[i],因为h[j]一定是比它更优的,所以我们可以用栈来维护序列,将大于等于h[i]的栈顶弹出。
题目大意:输入n个矩形高度,求最大矩形面积。
code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int n;
int h[N], stk[N], le[N], ri[N];
void get_len(int k[])
{
int tt = 0;
for (int i = 1; i <= n; i ++ )
{
while (tt > 0 && h[stk[tt]] >= h[i]) tt -- ;
k[i] = stk[tt];
stk[++ tt] = i;
}
}
int main()
{
while (cin >> n, n)
{
for (int i = 1; i <= n; i ++ ) cin >> h[i];
get_len(le);
reverse(h + 1, h + n + 1);
get_len(ri);
//这里注意h已经翻转了,所以le[]对应的要翻转一下!
long long ans = 0;
for (int i = 1, j = n; i <= n; i ++ , j -- )
ans = max(ans, 1ll * h[i] * (n - ri[i] - le[j]));
cout << ans << endl;
}
return 0;
}
本题思路:
本题让我们求不下降子序列构成的矩形的最大面积,我们可以统计出h[i, j]与h[i - 1, j]的大小关系,然后对于每一排跑一遍上面求正方图中最大矩形面积的代码然后取max即可。
code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e3 + 10;
int t;
int h[N][N];
int hh[N][N];
int n, m;
int stk[N], l[N], r[N];
void get_len(int q[], int ro)
{
int tt = 0;
for (int i = 1; i <= m; i ++ )
{
while (tt > 0 && hh[ro][stk[tt]] >= hh[ro][i]) tt -- ;
q[i] = stk[tt];
stk[++ tt] = i;
}
}
int main()
{
scanf("%d", &t);
while (t -- )
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &h[i][j]);
for (int i = 1; i <= n; i ++ )//转化为正方图问题
for (int j = 1; j <= m; j ++ )
if (h[i][j] >= h[i - 1][j]) hh[i][j] = hh[i - 1][j] + 1;
else hh[i][j] = 1;
//for (int i = 1; i <= n; i ++ )
// for (int j = 1; j <= m; j ++ ) printf("%d%c", hh[i][j], j == m ? '\n': ' ');
int ans = 0;
for (int i = 1; i <= n; i ++ )//对每一行求一次最大面积
{
get_len(l, i);
reverse(hh[i] + 1, hh[i] + m + 1);
get_len(r, i);
for (int j = 1, k = m; j <= m; j ++ , k -- )
{
ans = max(ans, hh[i][j] * (m - l[k] - r[j]));
//cout << hh[i][j] * (m - l[k] - r[j]) << ' ';
}
}
printf("%d\n", ans);
}
return 0;
}
5.1009 KD-Graph
思路:要求将点分成k组,然后组内存在len <= D的边,组间不存在len <= D的边。我们可以删除大于D的边,然后统计连通块个数即可,换个思路我们可以连接小于等于D的边,然后统计连通块个数,如果权值为w的所有边都连接完了此时连通块数目是k的话那么输出答案就是w。如果对于某个权值w,没连接它时连通块大于k个,连完之后小于k个,那么没有答案,输出-1。
code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int t;
int p[N];
int n, m, k;
struct node
{
int l, r, w;
bool operator< (const node& A)const
{
return w < A.w;
}
}seg[N * 5];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void solve()
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; i ++ ) scanf("%d%d%d", &seg[i].l, &seg[i].r, &seg[i].w);
for (int i = 1; i <= n; i ++ ) p[i] = i;
sort(seg + 1, seg + m + 1);
//for (int i = 1; i <= m; i ++ ) cout << seg[i].l << ' ' << seg[i].r << ' ' << seg[i].w << endl;
int cnt = n, ans = 0;
for (int i = 1; i <= m; i ++ )
{
if (seg[i].w != seg[i - 1].w)//与前面的不同权重,有答案则跳出
{
if (cnt == k)
{
printf("%d\n", ans);
return;
}
}
if (find(seg[i].l) == find(seg[i].r)) continue;//已经在一个连通块中
cnt -- ;//否则连通块数量 --
p[find(seg[i].l)] = find(seg[i].r);
ans = seg[i].w;
}
printf("%d\n", cnt == k ? ans : -1);
}
int main()
{
scanf("%d", &t);
while (t -- ) solve();
return 0;
}
标签:中超,int,tt,son,--,补题,ans,include,2021 来源: https://www.cnblogs.com/scl0725/p/15059667.html