北京信息科技大学第十三届程序设计竞赛暨ACM选拔赛

北京信息科技大学第十三届程序设计竞赛暨ACM选拔赛



A.lzh的蹦床


lzh的蹦床


根据贪心思想可以知道每一趟选择的出发点肯定是第一个不为1的数,但是如果一次次的模拟跳跃的话时间复杂度极高,那怎么处理呢?

我们可以来计算每一个蹦床对答案的贡献,然后相加即可。令sum[i]为左边跳到i的位置的次数,则在计算i位置的贡献时可分情况考虑:

如果 a [ i ] > 1 a[i]>1 a[i]>1时,它只会对[i+2,i+a[i]]这个区间产生一个+1的影响。

如果 s u m [ i ] ≥ a [ i ] sum[i]\ge a[i] sum[i]a[i]时,说明i位置已经变为1了,所以这个位置对答案也就没有贡献,但是对[i+1,i+a[i]]这个区间产生了影响,对[i+2,i+a[i]]这个区间产生了+1的影响,对i+1这个位置也产生了sum[i]-a[i]+1的影响

如果 s u n m [ i ] < a [ i ] sunm[i]<a[i] sunm[i]<a[i]时,有部分是左边传来的,这部分不能加入到答案的贡献中,只有剩下的a[i]-sum[i]-1才是i位置对答案的贡献。

那就最后就相当于一个区间修改操作和单点查询操作了,那什么可以解决勒?线段树、树状数组、差分都能解决。(线段树实测会T)


参考代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e3+10;
ll a[maxn],sum[maxn],n;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            sum[i]+=sum[i-1];
            if(sum[i]>=a[i])
            {
                if(i+1<=n)
                {
                    sum[i+1]+=sum[i]-a[i];
                    sum[i+2]-=sum[i]-a[i];
                    sum[i+1]++;
                    sum[min(n,i+a[i])+1]--;
                }
            }
            else
            {
                if(sum[i]==0)
                {
                    ans+=a[i]-1;
                    if(i+2<=n)
                    {
                        sum[i+2]++;
                        sum[min(n,i+a[i])+1]--;
                    }
                }
                else
                {
                    ll l=i+a[i]-sum[i]+1,r=i+a[i];
                    if(l<=n)
                    {
                        sum[l]++;
                        sum[min(n,r)+1]--;
                    }
                    ans+=a[i]-sum[i]-1;
                    if(i+2<=n)
                    {
                        sum[i+2]++;
                        sum[min(n,l-1)+1]--;
                    }
                }
            }
        }
        printf("%lld\n",ans);
    }
    system("pause");
    return 0;
}


B.所谓过河


所谓过河


总感觉我的时间复杂度会炸,但是学出来提交上去竟然只有7ms,绝了!!!

我想这的是将两两相交的圆用并查集维护起来,然后判断与y=0相交的圆是否和y=H相加的圆在一个集合里面。


参考代码:
#include<bits/stdc++.h>
using namespace std;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int maxn=1e4+10;
int s[maxn],n;
double h;
int sgn(double x)
{
	if (fabs(x) < eps)return 0;
	else return x < 0 ? -1 : 1;
}
int cmp(double x, double y)
{
	if (fabs(x - y) < eps||x < y) return 0;
	return 1;
}
struct Point {
	double x, y;
	Point() {}
	Point(double x, double y) :x(x), y(y) {}
	Point operator + (Point B) { return Point(x + B.x, y + B.y); }
	Point operator - (Point B) { return Point(x - B.x, y - B.y); }
	Point operator * (double k) { return Point(x * k, y * k); }
	Point operator / (double k) { return Point(x / k, y / k); }
};
struct Circle {
	Point c;
	double r;
	Circle() {}
	Circle(Point c, double r) :c(c), r(r) {}
	Circle(double x, double y, double _r) { c = Point(x, y), r = _r; }
}a[maxn];
double dis(Point A, Point B)
{
	return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
void init()
{
    for(int i=1;i<=n;i++)
    s[i]=i;
}
int find(int x)
{
    if(x!=s[x])s[x]=find(s[x]);
    return s[x];
}
void lh(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y)s[x]=s[y];
}
int main()
{
    scanf("%d%lf",&n,&h);
    init();
    for(int i=1;i<=n;i++)
    {
        double x,y,r;
        scanf("%lf%lf%lf",&x,&y,&r);
        a[i]={x,y,r};
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            double d=dis(a[i].c,a[j].c);
            if(cmp(d,a[i].r+a[j].r)==0)lh(i,j);
        }
    }
    vector<int>v1,v2;
    for(int i=1;i<=n;i++)
    {
        if(sgn(a[i].c.y-a[i].r)<=0)v1.push_back(i);
        if(a[i].c.y+a[i].r>=h)v2.push_back(i);
    }
    int flag=0;
    for(int i=0;i<v1.size();i++)
    {
        for(int j=0;j<v2.size();j++)
        {
            int x,y;
            x=find(v1[i]);
            y=find(v2[j]);
            if(x==y)
            {
                flag=1;
                break;
            }
        }
        if(flag==1)break;
    }
    if(flag==0)printf("No\n");
    else printf("Yes\n");
    system("pause");
    return 0;
}



C.旅行家问题1


旅行家问题1


没得思路,就判断起始点的位置,然后分情况求最小就行。


参考代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    int n,x;
    scanf("%d%d",&n,&x);
    ll minn=LLONG_MAX,maxx=LLONG_MIN;
    for(int i=1;i<=n;i++)
    {
        ll xx;
        scanf("%lld",&xx);
        minn=min(minn,xx);
        maxx=max(maxx,xx);
    }
    if(x<=minn)printf("%lld\n",maxx-x);
    else if(x>=maxx)printf("%lld\n",x-minn);
    else printf("%lld\n",min(2*(x-minn)+maxx-x,2*(maxx-x)+x-minn));
    system("pause");
    return 0;
}


D.旅行家问题2


旅行家问题2


贪心思想,可以知道对于每一座山只会到达一次和离开一次,所以n座山可以形成一个环。并且到达最高的山之后返回过程一定都是靠梯子来完成的,在到达最高山这个过程可能采用梯子,也有可能坐飞机。

为了方便计算,我们可以假设全部采用梯子,所以总和就是 ∑ x i \sum x_i xi,对于一些 h [ j ] > h [ i ] + x [ i ] h[j]>h[i]+x[i] h[j]>h[i]+x[i]的山只能采用坐飞机的方式,所以对答案另外的贡献就是 h [ j ] − h [ i ] − x [ i ] h[j]-h[i]-x[i] h[j]h[i]x[i],所以答案就是 ∑ x i \sum x_i xi+ ∑ h [ j ] − h [ i ] − x [ i ] \sum h[j]-h[i]-x[i] h[j]h[i]x[i],因为前面是固定的,要让答案最小则需要后面最小。在计算后面时只需要维护一个 h [ i ] + x [ i ] h[i]+x[i] h[i]+x[i]的最大值即可。


参考代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
int n;
struct node{
    ll h,x,maxh;
}a[maxn];
bool cmp(node xx,node yy)
{
    return xx.h<yy.h;
}
int main()
{
    scanf("%d",&n);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a[i].h,&a[i].x);
        ans+=a[i].x;
        a[i].maxh=a[i].h+a[i].x;
    }
    sort(a+1,a+1+n,cmp);
    ll maxx=a[1].maxh;
    for(int i=2;i<=n;i++)
    {
        if(a[i].h>maxx)ans+=(a[i].h-maxx);
        maxx=max(maxx,a[i].maxh);
    }
    printf("%lld\n",ans);
    system("pause");
    return 0;
}



E.小菲和Fib数列

小菲和Fib数列


对于 ( x i × x j + 1 ) (x_i\times x_j +1)%2 (xi×xj+1)这个式子可以知道,要想对答案有1的贡献就是 x i 、 x j x_i、x_j xixj全为偶数还在一个奇数一个偶数。

那这不就很简单了嘛,求出前n项的斐波拉契数,统计奇数和偶数,答案就是 e v e n × ( e v e n − 1 ) ÷ 2 + o d d × e v e n even\times (even-1)\div 2+odd\times even even×(even1)÷2+odd×even


参考代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e4+10;
ll a[maxn],sum1,sum2;
int n;
void init()
{
    a[1]=1,a[2]=1;
    sum1=2;
    for(int i=3;i<=n;i++)
    {
        a[i]=a[i-1]+a[i-2];
        a[i]%=2;
        if(a[i]==1)sum1++;
        else sum2++;
    }
}
int main()
{
    scanf("%d",&n);
    if(n==1)printf("0\n");
    else 
    {
        init();
        //cout<<sum1<<" "<<sum2<<endl;
        ll x,y;
        if(sum2<2)y=0;
        else y=(sum2-1)*sum2/2;
        x=sum1*sum2;
        printf("%lld\n",x+y);
    }
    system("pause");
    return 0;
}



F.好玩的音乐游戏

好玩的音乐游戏


没得什么思路,就模拟一下就行。


参考代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+10;
char a[maxn][10],op[10];
vector<int>v[maxn];
int n,m;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=7;j++)
        a[i][j]=' ';
    }
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%s%d%d",op,&x,&y);
        if(strcmp(op,"tap")==0)a[x][y]='O';
        else a[x][y]='X';
        v[x].push_back(y);
    }
    for(int i=1;i<=m;i++)
    {
        if(v[i].size()<=1)continue;
        sort(v[i].begin(),v[i].end());
        for(int j=v[i][0];j<=v[i][v[i].size()-1];j++)
        {
            if(a[i][j]==' ')a[i][j]='-';
        }
    }
    for(int i=m;i>=1;i--)
    {
        printf("|");
        for(int j=1;j<=7;j++)
        printf("%c",a[i][j]);
        printf("|\n");
    }
    printf("+-------+\n");
    system("pause");
    return 0;
}


G.ranko的手表

ranko的手表


就直接求出两个时间的所有取值,在可行的范围内求个最小值和最大值就行,纯模拟,没得技术含量


参考代码:
#include<bits/stdc++.h>
using namespace std;
char a[10],b[10];
vector<int>v1,v2;
void dfs_a(int st,int num,int sum)
{
    if(st==5)
    {
        v1.push_back(sum+num);
        return ;
    }
    if(a[st]==':')
    {
        sum=num*60;
        dfs_a(st+1,0,sum);
    }
    else if(a[st]!='?')dfs_a(st+1,num*10+(a[st]-'0'),sum);
    else
    {
        if(st==0)
        {
            for(int i=0;i<=2;i++)dfs_a(st+1,i,sum);
        }
        else if(st==1)
        {
            for(int i=0;i<=9;i++)
            dfs_a(st+1,min(23,num*10+i),sum);
        }
        else if(st==3)
        {
            for(int i=0;i<=5;i++)
            dfs_a(st+1,i,sum);
        }
        else if(st==4)
        {
            for(int i=0;i<=9;i++)
            dfs_a(st+1,min(59,num*10+i),sum);
        }
    }
}
void dfs_b(int st,int num,int sum)
{
    if(st==5)
    {
        v2.push_back(sum+num);
        return ;
    }
    if(b[st]==':')
    {
        sum=num*60;
        dfs_b(st+1,0,sum);
    }
    else if(b[st]!='?')dfs_b(st+1,num*10+(b[st]-'0'),sum);
    else
    {
        if(st==0)
        {
            for(int i=0;i<=2;i++)dfs_b(st+1,i,sum);
        }
        else if(st==1)
        {
            for(int i=0;i<=9;i++)
            dfs_b(st+1,min(23,num*10+i),sum);
        }
        else if(st==3)
        {
            for(int i=0;i<=5;i++)
            dfs_b(st+1,i,sum);
        }
        else if(st==4)
        {
            for(int i=0;i<=9;i++)
            dfs_b(st+1,min(59,num*10+i),sum);
        }
    }
}
int main()
{
    scanf("%s%s",a,b);
    dfs_a(0,0,0);
    dfs_b(0,0,0);
    sort(v1.begin(),v1.end());
    sort(v2.begin(),v2.end());
    int maxx=INT_MIN,minn=INT_MAX;
    for(int i=0;i<v1.size();i++)
    {
        for(int j=0;j<v2.size();j++)
        {
            if(v1[i]<v2[j])
            {
                maxx=max(v2[j]-v1[i],maxx);
                minn=min(minn,v2[j]-v1[i]);
            }
        }
    }
    printf("%d %d\n",minn,maxx);
    system("pause");
    return 0;                                                                                                                                                                
}


H.字母收集


字母收集


简单dp问题,状态方程也很容易推出来 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) + a [ i ] [ j ] dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j] dp[i][j]=max(dp[i1][j],dp[i][j1])+a[i][j],只需处理l、o、v、e这四个字母的值就行。

参考代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
int a[maxn][maxn],n,m;
int dp[maxn][maxn];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            char x;
            scanf(" %c",&x);
            if(x=='l')a[i][j]=4;
            else if(x=='o')a[i][j]=3;
            else if(x=='v')a[i][j]=2;
            else if(x=='e')a[i][j]=1;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            dp[i][j]=max(dp[i-1][j],dp[i][j-1])+a[i][j];
    }
    printf("%d\n",dp[n][m]);
    system("pause");
    return 0;
}


I.数字染色


数字染色


还没研究出来,老菜鸡了…




J.小红的心愿


小红的心愿


参考代码:
10



K.小红的树


小红的树


直接用sz[i]表示以i为根节点的子树的大小,则 s z [ i ] = ∑ s z [ j ] sz[i]=\sum sz[j] sz[i]=sz[j](j为i的儿子),先预处理所有的sz,询问时O(1)就能解决。


参考代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
char a[maxn];
int h[maxn],n,cnt,sz[maxn];
struct Edge{
    int to;
    int next;
}edge[maxn];
void add(int u,int v)
{
    edge[++cnt].to=v;
    edge[cnt].next=h[u];
    h[u]=cnt;
}
void dfs(int u)
{
    if(a[u]=='R')sz[u]=1;
    for(int i=h[u];~i;i=edge[i].next)
    {
        int j=edge[i].to;
        dfs(j);
        sz[u]+=sz[j];
    }
}
int main()
{
    scanf("%d",&n);
    memset(h,-1,sizeof(h));
    for(int i=2;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        add(x,i);
    }
    scanf("%s",a+1);
    dfs(1);
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int u;
        scanf("%d",&u);
        printf("%d\n",sz[u]);
    }
    system("pause");
    return 0;
}


L.重排字符串


重排字符串


简单构造题。先判断什么时候要输出no呢?输出no的情况应该是最多的出现次数大于 ( n + 1 ) / 2 (n+1)/2 (n+1)/2,其余的情况全是yes。

那yes的情况要怎样构造呢?因为要相邻字符不同,则可以先将出现次数最多的字符从1好位置开始放置,然后每一次都放置在+2的位置上。由于每一个位置都可以由 1 + 2 x 或 者 2 + 2 x 1+2x或者2+2x 1+2x2+2x表示,所以在放完1+2x的情况后,可将剩下的字符全部放在2+2x这些位置上,这就保证了相邻字符不同的情况。


参考代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
#define PI pair<int,char>
int vis[maxn];
PI p[30];
int n;
bool cmp(PI x,PI y)
{
    return x.first>y.first;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<26;i++)
    p[i]={0,'a'+i};
    for(int i=1;i<=n;i++)
    {
        char op;
        scanf(" %c",&op);
        p[op-'a'].first++;
    }
    sort(p,p+26,cmp);
    if(p[0].first>(n+1)/2)printf("no\n");
    else
    {
        int idx=1;
        memset(vis,-1,sizeof(vis));
        for(int i=0;i<26;i++)
        {
            if(p[i].first==0)break;
            while(p[i].first!=0)
            {
                if(vis[idx]==-1)
                {
                    vis[idx]=p[i].second-'a';
                    p[i].first--;
                }
                idx+=2;
                if(idx>n)idx=2;
            }
        }
        printf("yes\n");
        for(int i=1;i<=n;i++)
        {
            char x='a'+vis[i];
            printf("%c",x);
        }
        printf("\n");
    }
    system("pause");
    return 0;
}

如有问题,欢迎大佬私信!!!!!!!!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值