西南科技大学信息学院第十三届程序设计竞赛感想

这次程序设计大赛,是我们学长和以及我们的金牌老学长出的题,这次我没有参赛(可能太菜了,,,哈哈)

我作为工作人员,也是见证了大家的奋斗,大家都挺强的,也看见了我们大一同学的成长,虽然没有拿到最佳新人奖。。。

其实挺高兴的。

本次比赛前,我并不知道题,在比赛开始了,我也没有拿到题,就到了一个暂时还没有人到的位置上(因为时间实在太紧了,遇上了计算机学院的一些同学参加考试,我们信息学院也是万分无奈啊。。。,但不过我看大家都一个小时不到提前交卷了,影响不大,哈哈哈),拿了一本题册开始看起来

刚刚开始因为问题比较多,而且又没有仔细看题,还以为这次的题太难了。后来发现签到题还是听多了,我也打算在这篇博客慢慢更新院赛的题解,并且在分享一下当时我的心情吧,哈哈哈,最近比较忙,而且作业都没有写,可能更新比较慢。

挂上题目连接https://www.oj.swust.edu.cn/problem

先来一道没有多少人过的题吧

比赛的G题 小Z的糖果难题

这道题比赛没有多少队伍过了,还有的队伍被卡了数学的log心痛半秒,这道题比赛时我没有怎么想,听他们说用倍增就只想到了,倍增的做法

dp[i][j]表示比i这个位置大的第2的j次方的数的位置,dp[i][j]=dp[dp[i][j-1]][j-1]可能比较绕,多读一下。

为什么这样做呢,因为最先想到的是dp表示数目,发现禁不起推敲,后来听了题解就更加确定是表示位置的,很多同学在想为什么会这么想,我就来瞎扯一下吧,我觉得这道题肯定是nlogn的算法,确实出题人说了,为了也是让nlognlogn的算法过,你既然要logn出现就只有二分哪些了,而且与区间处理相关的就一个rmq,进行区间长度的二分了,然后你在设计的时候就要这样想,弄一个,其实我觉得,不好想,你从dp方程可能会看出来,是从大到小推的,所以这些数就有一定的大小关系,所以查找时,从大到小查,然后找到第一个,在已他为起点,这样查找,可以用递归,也可以用递推,递推我挂了几次。。。

另外倍增算法依然很慢,而且我还开了挂。。。。正解应该是Sy队和Ly队的树状数组维护下标的离线算法,我太弱了想不到,后来补上吧,他们tql

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define cppiofast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
inline bool scan_d(int &num)
{
    char in; bool IsN = false;
    in = getchar();
    if (in == EOF) return false;
    while (in != '-' && (in<'0' || in>'9')) in = getchar();
    if (in == '-'){ IsN = true; num = 0; }
    else num = in - '0';
    while (in = getchar(), in >= '0'&&in <= '9'){
        num *= 10, num += in - '0';
    }
    if (IsN) num = -num;
    return true;
}
void out(int x)
{
    if (x<0){
        putchar('-'); out(-x);
        return;
    }
    if (x>9) out(x / 10);
    putchar(x % 10 + '0');
}
const int N=1e5+10;
int dp[N][18],n,q,a[N];
int qu[N];
int main()
{
    int T;
    scan_d(T);
    while(T--)
    {
        scan_d(n);
        scan_d(q);
        int M=18;
        for(int i=1;i<=n;i++)
        {
            scan_d(a[i]);
        }
        memset(dp,0,sizeof(dp));
        //queue<int>qu;
        int tail=0;
        for(int i=n;i>=1;i--)
        {
            while(tail>0&&a[qu[tail]]<=a[i])
                tail--;
            qu[++tail]=i;
            if(tail>=2)
                dp[i][0]=qu[tail-1];
        }
        for(int i=n;i>=1;i--)
        {
            for(int j=1;j<M;j++)
            {
                if(dp[i][j-1]==0)
                    break;
                dp[i][j]=dp[dp[i][j-1]][j-1];
            }
        }
        while(q--)
        {
            int l,r,ans=1;
            scan_d(l);scan_d(r);
            for(int i=l;i<=r;i++)
            {
                int flag=0;
                for(int j=M-1;j>=0;j--)
                {
                    if(dp[i][j]<=r&&dp[i][j]!=0)
                    {
                        flag=1;
                        ans+=(1<<j);
                        i=dp[i][j]-1;
                        break;
                    }
                }
                if(!flag)
                    break;
            }
            out(ans);
            printf("\n");
        }
    }
    return 0;
}

再来一道题,虽然没有多少人做,表面吓人的题吧

C题 Alice与Bob

这道题不知道为什么没有多少人做,我觉得大一的新生,如果仔细一点,其实可以做的,是不是看见没有人做,但不过我还是看见一个女生队,在做这道题,但不过由于做题的人开得题太分散了,而且很多大一新生不会跟榜,有点分不清题目层次,但ACM就是这样残酷的,我们这种蒟蒻只有跟着大佬的步伐,这道题题解说的是,可以用FFT做,但不过我觉得大部分都不会把,那就把公式展开吧,其实就是一个双重求和,用前缀和。当时比赛时由于比较忙并没有看出来,今天一试发现也是一道签到题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define cppiofast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N=5e5+10;
ll a[N],b[N],sum1[N],sum2[N];

int main()
{
    int T,n,m;scanf("%d",&T);
    while(T--)
    {
        ll sumb=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%lld",&b[i]);
            sumb+=b[i]*b[i];
        }
        sum1[0]=sum2[0]=0;
        for(int i=1;i<=n;i++)
        {
            sum1[i]=sum1[i-1]+a[i];
            sum2[i]=sum2[i-1]+a[i]*a[i];
        }
        ll ans=0;
        for(int i=1;i<=m;i++)
        {
            ans=ans+sum2[n-m+i]-sum2[i-1];
            ans=ans-2*b[i]*(sum1[n-m+i]-sum1[i-1]);
        }
        ans+=sumb*(n-m+1);
        printf("%lld\n",ans);
    }
    return 0;
}

今天再补一道,比赛没有人过的题吧

B题 小Z的糖果店

这道题目其实裸想,可以想到的,但不过代码量有点大,他们写了150行,我写了190行。。。。

题目思路就是先找到一个中心点,这个点猜都可以猜到在树的直径上,可以用反证法证明,我也没证过。。。。

树的直径的求法,随便找一个点跑dfs找到一个距离最远的点,然后在用这个点跑dfs在找到一个最远的点,这个两个点就是树的直径的端点。然后在跑dfs找到直径上的所以点,然后遍历直径上的点,找出到两个端点的距离的最大值的最小值。就可以了。

找到了后,再来一次dfs找到以中心点为根搜索下去以每一个点为根到所有子节点到中心点的最大距离

然后再跑一个最大权的bfs,依次删除与增加边得到最佳答案。比赛的时候,他们说老学长想到了怎么做,但不过后来又没有写了。。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define cppiofast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
const int N=1e5+10;
const int inf=0x3f3f3f3f;
struct node
{
    int v,w,nxt;
}edge[N<<1];
pair<ll,int>pa;
int cnt,head[N<<1],n,k;
vector<int>vec;
bool flag=false;
void init()
{
    memset(head,-1,sizeof(head));
    cnt=0;
    vec.clear();
    flag=false;
}
void add(int u,int v,int w)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].nxt=head[u];
    head[u]=cnt++;
}
int vis[N],st1,st2;
ll dis1[N],dis2[N];
void dfs1(int u)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(!vis[v])
        {
            dis1[v]=dis1[u]+edge[i].w;
            dfs1(v);
        }
    }
}
void dfs2(int u)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(!vis[v])
        {
            dis2[v]=dis2[u]+edge[i].w;
            dfs2(v);
        }
    }
}
void dfs3(int u)
{
    vis[u]=1;
    if(u==st2){
        flag=true;
        return;
    }
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        if(!vis[v])
        {
            vec.push_back(v);
            dfs3(v);
            if(flag)
                return;
            vec.pop_back();
        }
    }
}
ll ans=0,dis[N];
ll dfs4(int u,int fa,ll dd)
{
    ll maxlen=0;
    for(int i=head[u];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        int d=edge[i].w;
        if(v==fa)
            continue;
        maxlen=max(maxlen,dfs4(v,u,d));
    }
    return dis[u]=dd+maxlen;
}
void bfs(int cp)
{
    memset(vis,0,sizeof(vis));
    priority_queue<pair<ll,int> >qu;
    vis[cp]=1;
    for(int i=head[cp];i!=-1;i=edge[i].nxt)
    {
        int v=edge[i].v;
        qu.push(make_pair(dis[v],v));
        vis[v]=1;
    }
    k=k-1;
    while(!qu.empty())
    {
        if((k--)==0)
            break;
        pair<ll,int> now;
        now=qu.top();qu.pop();
        int u=now.second;
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        {
            int v=edge[i].v;
            if(!vis[v])
            {
                vis[v]=1;
                qu.push(make_pair(dis[v],v));
            }
        }
    }
    if(qu.empty())
        printf("0\n");
    else
        printf("%lld\n",qu.top().first);
}
int main()
{
    int T;scanf("%d",&T);
    int u,v,w;
    while(T--)
    {
        init();
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        if(n==1)
        {
            printf("0\n");
            continue;
        }
        memset(dis1,0x3f,sizeof(dis1));
        memset(vis,0,sizeof(vis));
        dis1[1]=0;
        dfs1(1);
        ll maxn=0;
        for(int i=1;i<=n;i++)
        {
            if(dis1[i]>maxn)
            {
                maxn=dis1[i];
                st1=i;
            }
        }
        memset(vis,0,sizeof(vis));
        memset(dis2,0x3f,sizeof(dis2));
        dis2[st1]=0;
        dfs2(st1);
        maxn=0;
        for(int i=1;i<=n;i++)
        {
            if(dis2[i]>maxn)
            {
                maxn=dis2[i];
                st2=i;
            }
        }
        memset(vis,0,sizeof(vis));
        vec.push_back(st1);
        dfs3(st1);
        int mark;ll mx,mn=1e18;
        for(int i:vec)
        {
            mx=max(dis2[st2]-dis2[i],dis2[i]);
            if(mn>mx)
            {
                mn=mx;
                mark=i;
            }
        }
        memset(dis,0,sizeof(dis));
        dfs4(mark,-1,0);
        bfs(mark);
    }
    return 0;
}

 

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值