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(待)

题意:

题解:

注意:

代码:

posted @   Aaryn21  阅读(179)  评论(0编辑  收藏  举报
编辑推荐:
· 从 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生成工具
点击右上角即可分享
微信分享提示