2021“MINIEYE杯”中国大学生算法设计超级联赛(1)
2021“MINIEYE杯”中国大学生算法设计超级联赛(1)
1001 Mod, Or and Everything
题意:
对于每次输入的数字n,求(n%1)|...(n%n)的值
题解:
n%(n-i)=i,n偶数:m=(n/2-1)||n奇数:m=(n-1/2);0<=i<=m =>0|1|2|...|m
注意:
可以根据打表可知0|1|2...|m=pow(2,m上的最高数位)-1
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t;cin>>t;
while(t--){
ll n;cin>>n;ll ans;
int i;ans=1;
for(i=1;i<=n;i++){
if(ans>=n)break;
else ans*=2;
}
i--;//n的最高数位
ans=pow(2,i-1)-1;//m的最高数位
cout<<ans<<endl;
}
return 0;
}
1002 Rocket land(待)
题意:
题解:
注意:
代码:
1003 Puzzle loop(待)
题意:
题解:
注意:
代码:
1004 Another thief in a Shop(待)
题意:
题解:
注意:
代码:
1005 Minimum spanning tree
题意:
求一个最小生成树
题解:
因为这棵树有一些特征,就是如果某个数是质数,就在前一个数的权值上加上自己x2,否则就加上自己x1。所以可以通过一个质数筛选出所有的质数,然后遍历,用一个数组存下来得到的值,之后只要直接读取就好了。
注意:
初始数据是down[2]=0
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7;
long long prime[maxn],down[maxn];bool v[maxn];
void primes()
{
memset(v,0,sizeof(v));
int m=0;//质数
for(int i=2;i<maxn;i++)
{
if(!v[i])prime[++m]=i;//质数
for(int j=1;j<=m&&prime[j]*i<=maxn;j++)
{
v[prime[j]*i]=true;//将倍数都赋给那些为合数的值
if(i%prime[j]==0)break;//已经进行过计算了
}
}
//for(int i=1;i<=m;i++)cout<<prime[i]<<" ";
}
int main(){
int t,n;
cin>>t;
primes();
down[2]=0;
for(int i=3;i<=maxn;i++){
if(v[i]==true)down[i]=down[i-1]+i;
else down[i]=down[i-1]+2*i;
}
while(t--)
{
cin>>n;
printf("%lld\n",down[n]);
}
return 0;
}
1006 Xor sum
题意:
找一个连续字串异或和大于等于k,有多组则输出左边起始点最小的字串左右序号
题解:
先求一个异或前缀和x,因为aba=b.所以某一段子串(n~m)的连续异或和就为x[n]^x[m],这里采用字典树的方式来维护运行,将前缀和用二进制的方式展开,插入字典树里边,去找两个数的前缀和异或>=k,记录下对应的编号,遍历下去找到最小的长度即可。
注意:
因为是多组输入,所以trie[][]数组里头有可能会有之前留下的数字,所以会造成一些问题,所以要进行初始化,但是用memset会T,所以这里采用,将下一个结点的trie[][]=0的形式
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
#define INF 0x3f3f3f3f
int trie[maxn*31+10][2];//将一个数转换成二进制然后丢进字典树里边去维护
int index[maxn*31+10];//对于每一个数来说他们的二进制都是31位数的形式,所以插入的时候,是一个个把他们的数位插进去,所以tot会最多加到maxn*31次
int a[maxn];
int n,m,tot;
void insert(int x,int in)
{
int p=1;
for(int k=30;~k;k--)//(-1取反为0,可作为判断条件)
{
int ch=(x>>k)&1;
if(!trie[p][ch]){
trie[p][ch]=++tot;//trie开始用的就是1,所以要往下继续找结点,即2
trie[tot][0]=trie[tot][1]=0;//下一个结点是空的
}
p=trie[p][ch];
index[p]=in;//表示这一系列的数都是第in个数的插入结果
}
}
int find(int x)//得到的是与x异或的另外一个数的最小位置
{
int p=1;int res=0;
for(int k=30;~k;k--) {
int ch = (x >> k) & 1;
if (trie[p][ch ^ 1])//遍历的是另外一个数位和x完全不一样的数字,如果那个数字存在的话,就可以直接加上1<<k
{
p = trie[p][ch ^ 1];
res += 1 << k;
if (res >= m)return index[p];
}
else p=trie[p][ch];
}
return -1;
}
void solve()
{
scanf("%d%d",&n,&m);tot=1;
a[0]=0;int len=n+1,l,r;
trie[1][0]=trie[1][1]=0;
index[1]=1;//初始状态
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]>=m&&len!=1){
len=1;
l=r=i;
}
a[i]^=a[i-1];
}
if(len==1){
printf("%d %d\n",l,r);
return ;
}
int mit=INF;
for(int i=1;i<=n;i++)
{
int t=find(a[i]);//因为前一个数已经被插入进去了,所以直接搜就好了
if(~t&&i-t<mit)
{
mit=i-t;
l=t+1;
r=i;
}
insert(a[i],i);
}
if(mit==INF)printf("-1\n");
else printf("%d %d\n",l,r);
}
int main()
{
int t;
cin>>t;
while(t--)
{
solve();
}
}
1007 Pass!(待)
题意:
题解:
注意:
代码:
1008 Maximal submatrix
题意:
求连续的从上往下不递减的最大子矩阵的面积
题解:
悬线法,通过悬线的方式,左右移动找到最大的边界,用长度乘以悬线长度,即可求出面积
单调栈
注意:
开数组,稍微开大一点,这样不会越界
代码:
#include<bits/stdc++.h>
using namespace std;
int t,n,m;
const int maxn=2e3+5;//注意数组不要开太小,数组越界会显示T
int mt[maxn][maxn],k[maxn][maxn],l[maxn],r[maxn];
void solve()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&mt[i][j]);
for(int i=1;i<=m;i++)
{
stack<int>s;
for(int j=1;j<=n;j++)
{
if(s.empty()||mt[j][i]>=mt[s.top()][i])s.push(j);//连续不递减
else {
while(s.size()){
k[s.top()][i]=j-1;//因为到第j位出现断层,所以,每一条这样的悬线,他们的最长延伸长度都是j-1
s.pop();
}
s.push(j);//继续递归下去
}
}
while(s.size())//如果遍历到了末端,就说明,他们到了矩形的边界处
{
k[s.top()][i]=n;
s.pop();
}
for(int j=1;j<=n;j++)
{
k[j][i]-=j-1;//表示的是从底线j上边满足不递减的长度
}
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
l[j]=r[j]=j;//表示的是每条悬线所能移动的左右边界,滚动数组,减少空间
}
for(int j=1;j<=m;j++){
while(l[j]>1&&k[i][l[j]-1]>=k[i][j])//说明在第-1存在着一个障碍物
l[j]=l[l[j]-1];//所以它的左边界就要左移
}
for(int j=m;j;j--){
while(r[j]<m&&k[i][r[j]+1]>=k[i][j])
r[j]=r[r[j]+1];//l[j]==1||r[j]==m,表示在边界处,不需要再进行处理了
}
for(int j=1;j<=m;j++)
ans=max(ans,(r[j]-l[j]+1)*k[i][j]);
}
printf("%d\n",ans);
}
int main()
{
cin>>t;
while(t--)
{
solve();
}
return 0;
}
1009 KD-Graph
题意:
将一些点分成k块,并满足每一块里边都存在<=D,块与块之间满足>=D
题解:
最小生成树,通过不断寻找权值
注意:
并查集利用一下路径压缩,就不会T了
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
const int maxn=1e6+10;
struct node{
int u,v,w;
//node(int _u,int _v,int _w):u(_u),v(_v),w(_w){}
}edge[maxn];
int f[maxn];
bool cmp(node a ,node b)
{
return a.w<b.w;
}
inline int find(int x)
{
return (x==f[x]?x:(f[x]=find(f[x])));//这里有一个路径压缩
//return x==f[x]?x:find(f[x]);
}
signed main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
}
sort(edge+1,edge+m,cmp);
int now=n,ans=0,flag=0;//????
for(int i=1;i<=m;i++)//权值是从小到大排列的
{
if(edge[i].w!=edge[i-1].w){//说明这个得到的ans就是上一组的权值
if(now==k){//已经分成了k块
printf("%d\n",ans);
flag=1;
break;
}
}
if(find(edge[i].u)==find(edge[i].v))continue;
now--;
f[find(edge[i].u)]=find(edge[i].v);//合并
ans=edge[i].w;//得到更大的权值,毫无疑问之前的分组的权值都是小于ans的
//而其他没有并进这些块的组很明显他们之间的权值都是要大于ans的
}
if(!flag){//表示中间有好几个相同权值的点,但是不知道他们有没有被并进去,需要判断
printf("%d\n",now==k?ans:-1);
}
}
return 0;
}
1010 Zoto
题意:
注意理解题意,这里是说有n次输入,每次输入一个数(i,a[i])作为点的x,y坐标,再进行m次查询,给定两个点的横坐标和纵坐标问查询的矩阵区域里头,有多少个y坐标不同的数。
题解:
了解了莫队算法之后,这道题目就可以转换成求一个区间内不同的数的个数了
很轻而易举了,但是很明显还减去那些不在y1~y2之间的数,首先我们想到的就是直接暴力,对于l~r之间的点遍历,看他们是否在y1~y2这个区间里头,进行处理。没错没错,那肯定T了,所以作为一个有志向的Tlemer,我们要学会一些巧妙的算法,比如说树状数组,用树状数组来维护这个y坐标范围,getsum(y2)-getsum(y1-1)即是我们想要得到的答案
注意:
因为在树状数组里头都是以0作为退出while循环的判断数,所以对于y坐标而言,不能出现0的坐标,所以我们可以考虑将所有的y坐标+1.这样相对而言是没有变化的。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 5;
#define INF 0x3f3f3f3f
int block[maxn], cnt[maxn], a[maxn], c[maxn], res[maxn];
int t, n, m, s, l, r, num, unit;
int lowbit(int x) {
return x & (-x);
}
void updata(int i, int k) {
while (i <= n) {
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i) {
int res = 0;
while (i > 0) {
res += c[i];
i -= lowbit(i);
}
return res;
}
void add(int x) {
if (!cnt[a[x]]) updata(a[x], 1);//用y坐标更新维护
cnt[a[x]]++;
}
void del(int x) {
cnt[a[x]]--;
if (!cnt[a[x]]) updata(a[x], -1);
}
struct query {
int l, r, n, ans, yl, yr;
} Q[maxn];
int cmp1(query a, query b) {
return (block[a.l] ^ block[b.l]) ? block[a.l] < block[b.l] : ((block[a.l] & 1) ? a.r < b.r : a.r > b.r);
}//更快,对于左端点在同一奇数块的区间,右端点按升序排列,反之降序。
int main() {
scanf("%d", &t);
while (t--) {
l = 1, r = 0;
scanf("%d%d", &n, &m);
s = sqrt(n);
unit = n / sqrt(m);
memset(cnt, 0, sizeof(cnt));
memset(c, 0, sizeof(c));
num = ceil((double) n / s);
for (int i = 1; i <= num; i++) {
for (int j = (i - 1) * s + 1; j <= s * num; j++)block[j] = i;
}
for (int i = 1; i <= n; i++)scanf("%d", &a[i]), a[i]++;//注:y坐标都加1是为了使y=0的情况不出现,因为y等于0的时候无法进行循环
for (int i = 1; i <= m; i++) {
scanf("%d%d%d%d", &Q[i].l, &Q[i].yl, &Q[i].r, &Q[i].yr);
Q[i].n = i;
Q[i].yl++, Q[i].yr++;
}
sort(Q + 1, Q + m + 1, cmp1);
for (int i = 1; i <= m; i++) {
while (l < Q[i].l)del(l++);
while (l > Q[i].l)add(--l);
while (r > Q[i].r)del(r--);
while (r < Q[i].r)add(++r);
res[Q[i].n] = getsum(Q[i].yr) - getsum(Q[i].yl - 1);
}
for (int i = 1; i <= m; i++)printf("%d\n", res[i]);
}
return 0;
}
1011 Necklace of Beads(待)
题意:
题解:
注意:
代码:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具