2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)题解(7道题)

只有:B,D,E,G,I,K,L

正文

B:题目链接  Error

题目:

思路:二分

二分给出一个mid,让第一个数 r 为max(a[1]-d,0),以后的数至少是 r+1,而且太小就直接返回NO,也就是 if( a[i]+mid < r+1) return 0;

因为递增,r最小为 r+1。因为差值小于mid,r最小为 a[i]-mid ,所以 r=max(r+1,a[i]-mid);

int check(int d){
    int r=max(0,a[1]-d);        //题意给的是正整数,应该是max(1,a[1]-d),数据估计没卡这点
    fo(2,n){
        if(a[i]+d<r+1) return 0;
        r=max(r+1,a[i]-d);
    }
    return 1;
}

总代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
#define ll long long
#define M 1000005
int n;
int a[M];
int check(int d){
    int r=max(0,a[1]-d);
    fo(2,n){
        if(a[i]+d<r+1) return 0;
        r=max(r+1,a[i]-d);
    }
    return 1;
}
int main()
{
    cin>>n;
    fo(1,n) cin>>a[i];
    int l=1,r=1e9,ans=-1;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid)){
            r=mid-1;
            ans=mid;
        }
        else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

 D:题目链接  树的果实

 题目:

思路: 树的dfs序+莫队

这题属实是没想到出个莫队,想到莫队的话难度不是很大,树遍历dfs序使一个点的子树成为连续的区间。

查询一个节点的子树值,就是查询一个连续区间的值,用普通莫队就可以做出,细节就不多说了,因为这里和莫队模板题很接近了

总代码:

#include<bits/stdc++.h>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define ll long long
#define int long long
#define M 400010
#define ll long long
int n,m,k,s=0,v,i,j;
int res=0;
struct Node
{
    int id,l,r;
    bool operator<(const Node temp)const{
        if(l/v==temp.l/v) return r<temp.r;
        return l<temp.l;
    }
}x[M];
struct node
{
    vector<pair<int,int>>v;
}xx[M];
int a[M],ans[M],vis[M];
int in[M],out[M],cnt=0;
void dfs(int d,vector<pair<int,int>>&v,int pre)
{
    int l=v.size();
    in[d]=++cnt;
    a[cnt]=v[i].second;
    for(int i=0;i<l;i++){
        if(v[i].first!=pre){
            dfs(v[i].first,xx[v[i].first].v,d);
        }
    }
    out[d]=cnt;
}
void add(int d){
    vis[d]++;
    res+=d*d*(vis[d]*vis[d]-(vis[d]-1)*(vis[d]-1));
}
void del(int d){
    res+=d*d*(-vis[d]*vis[d]+(vis[d]-1)*(vis[d]-1));
    vis[d]--;
}
int solve(int l,int r){
    while(i<l) del(a[i++]);
    while(i>l) add(a[--i]);
    while(j<r) add(a[++j]);
    while(j>r) del(a[j--]);
    return res;
}
signed main()
{
    cin>>n>>m>>k;
    v=sqrt(n*1.0);
    for(int a,b,c,i=1;i<n;i++){
        scanf("%lld%lld%lld",&a,&b,&c);
        xx[a].v.push_back(make_pair(b,c));
        xx[b].v.push_back(make_pair(a,c));
    }
    dfs(1,xx[1].v,0);
    for(int i=1;i<=m;i++){
        int temp;
        scanf("%lld",&temp);
        x[i].l=in[temp]+1;
        x[i].r=out[temp];
        x[i].id=i;
    }
    sort(x+1,x+m+1);
    i=1,j=0,res=0;
    for(int i=1;i<=m;i++) ans[x[i].id]=solve(x[i].l,x[i].r);
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}

 说过子树一定用dfs序,过程中还是想偏了

 E:题目链接  吃利息

题目:

思路:简单模拟

题意看懂问题就不大,大概是每回合两笔收入,一个是5,一个是n/10,而后者最多是5。用循环实现即可

#include<iostream>
#include<algorithm>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
#define dou double
#define EXP 1e-8
#define ll long long
#define M 1000005
int t;
ll n,m,sum=0;
ll a[M];
int main()
{
    cin>>n>>m;
    while(m--){
        n+=(5+min((ll)5,n/10));
    }
    cout<<n<<endl;
	return 0;
}

F:题目链接  Star

题目:

这题是最亏的,大概率用到极角排序求凸包,最后求凸包面积吧,但是思路只有时间复杂度为2^50的暴力搜索,可能再加点凸包封闭不再搜索的优化,太麻烦了码一半自己放弃了。

但是看完唯一的视频题解,样例怎么都没法过,大概思路就是贡献思维,一个边能被贡献上只有右边没有一个点可以理解,但是边贡献的面积是什么都没听懂。

枚举三角形要么多情况,要么少情况,以后有机会会了再更吧

G:题目链接  MP4

题目:

思路:思维+sort

首先字符后面.mp4其实是不影响顺序的,就是数字的字典序排序,n如果很大的话,比如n为100100。可以发现,前面的都是1,10,100,1000,....,100000,100001,100002,100003.....

也就是从s=1开始,每*10添加往后50个数字,s+0,s+1,s+2...s+50,将这些不多的字符串进行sort排序输出即可,也就是保证答案被收录,剩下的交给sort。

这是比赛时保证不WA的代码,再精简情况也是完全可以的

int s=1;
while(s<n){
    fo(0,55){
        if(s+i<s*10&&s+i<=n){        //添加可能为答案的数
            add(s+i);
        }
    }
    s*=10;
}

 总代码:

#include<bits/stdc++.h>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
#define ll long long
#define M 100005
int n,l=0;
string s[M];
void add(int d)
{
    l++;
    while(d){
        s[l]+='0'+d%10;
        d/=10;
    }
    reverse(s[l].begin(),s[l].end());
    s[l]+=".mp4";
}
int main()
{
    cin>>n;
    if(n<10000){
        fo(1,n){
            add(i);
        }
    }
    else{
        int s=1;
        while(s<n){
            fo(0,55){
                if(s+i<s*10&&s+i<=n){
                    add(s+i);
                }
            }
            s*=10;
        }
    }
    sort(s+1,s+l+1);
    fo(1,50){
        cout<<s[i]<<endl;
    }
    return 0;
}

  I:题目链接  展览

题目:

 

 思路:线性基

这题属实离谱,要是只选两个的话,妥妥的字典树

然而这是线性基的模板题,那就没什么好说的了,下面是网上搜的最短的代码,还是自己学一下这个算法比较好

总代码:

#include<cstdio>
using namespace std;
typedef long long ll;
const int N=50+5,Maxbit=50;
ll p[N+1];
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i){
        ll x;scanf("%lld",&x);
        for(int j=Maxbit;j>=0;--j){
            if(!((x>>j)&1)) continue;
            if(!p[j]) {p[j]=x;break;}
            x^=p[j];
        }
    }
    ll ans=0,t;
    for(int j=Maxbit;j>=0;--j){
        t=ans^p[j];
        if(t>ans) ans=t;
    }
    printf("%lld\n",ans);
}

   K:题目链接 礼物

题目:

 

思路:简单模拟

思路就是求最大值,sort或者挨个比较都行,签到题

总代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define fo(a,b) for(int i=a;i<=b;i++)
#define inf 0x3f3f3f3f
#define dou double
#define EXP 1e-8
#define ll long long
#define M 1000005
int t;
int n;
int a[M];
int main()
{
    cin>>n;
    fo(1,n){
        cin>>a[i];
    }
    sort(a+1,a+n+1);
    cout<<a[n]<<endl;
	return 0;
}

    L:题目链接 看错题

题目:

 

思路:思维

这题代码不需要线段树的,但是不会线段树题意是很难懂的,要明白任何时候这个满线段树都是合法的线段树,即顶点=两个子点的和,加子点多少,顶点也加多少,但顶点仍是两子点和

那么所有子点往上走,路程和,也就是答案 =(所有子点和)*(2 ^ 层数-1)

 这个图的答案就是

1+3+10        +        2+3+10        +        3+7+10        +        4+7+10

1+2+3+4  就是所有子点和=10,(3+7)+(3+7)=所有子点和*2,10+10+10+10=所有子点和*4。

最后就是(所有子点和)*(1+2+4)=(所有子点和)*(2 ^ 层数-1)

层数是不变的,(2 ^ 层数-1)就= 2*n-1

所以只求所有子点和,那就很简单了,累加ans,每次变化  ans+=z*(y-x+1);

scanf("%lld%lld%lld",&x,&y,&z);
ans+=z*(y-x+1);

总代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define M 1000005
#define lowbit(a) ((a)&(-a))
#define ll long long
ll n,m,a;
ll ans=0;
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a);
        ans+=a;
    }
    for(int i=1;i<=m;i++)
    {
        ll x,y,z;
        scanf("%lld%lld%lld",&x,&y,&z);
        ans+=z*(y-x+1);
        printf("%lld\n",ans*(n*2-1));
    }
    return 0;
}

 最后

可以发现前面的是思维,这个是比较接近正式赛的。后面的算法题就很多了,大多是偏算法模板题的。

猜你喜欢

转载自blog.csdn.net/m0_58177653/article/details/123773699