2023NYIST(计科ACM&TC)第五次招新赛——题解

 一:整体评价

       出题难度

这场本来就是想多出点之前的算法的,没想到会打成这样,刚好让你们意识到自己算法也要多练。

这场都打得很差感觉,差距太大了,自己慢慢补吧。算法也不能忘记。

二:题解

A 最最最最长(我真的好长)~~~~~~~上升子序列咧咧咧咧

思路

        

本题是对大家思维的一个考察。我们不难想到如果把b序列逆序插入对于a序列来说影响是较小的,会使得对a序列的贡献最多为1。那我们同时要考虑,如何把这个贡献1也消掉。

那我们不妨把a序列中的元素和b序列中的元素逐一进行比较,谁大谁就先插入,从而做到对a数组的贡献最小。

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=998244353;
const int MX=0x3f3f3f3f3f3f3f3f; 
int n,m;
int b[500005];
int a[500005];
bool cmp(int x,int y){
    return x>y;
}
void icealsoheat(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int j=1;j<=m;j++){
        cin>>b[j];
    }
    sort(b+1,b+1+m,cmp);
    int id=1;
    for(int i=1;i<=n;i++){
        while(id<=m&&b[id]>=a[i]){
            cout<<b[id]<<" ";
            id++;
        }
        cout<<a[i]<<" ";
    }
    while(id<=m){
        cout<<b[id]<<" ";
        id++;
    }
    cout<<"\n";

    
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie();
    cout.tie();
    int _yq;
    _yq=1;
    cin>>_yq;
    while(_yq--){
        icealsoheat();
    }
}

B 这该用啥算法?

思路

        这题主要就是贪心二分,因为如果有一个x可以放置那么说明对于更小的数也可以所以说是满足二分的性质的,然后知道二分之后就要像check函数怎么写了,贪心的想最优解肯定是从后往前跑,直接把多余的分出去,在分第i个的时候如果前面分的已经够x个了那就把原来的全分过去,如果不够就分多余的分过去,然后最后跑一遍判断是否都有至少x个。

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int a[N];
int b[N];
int n;
int check(int x){
    for(int i=1;i<=n;i++)b[i]=0;
    for(int i=n;i>=3;i--){
        if(b[i]>x){
            int tmp=a[i]/3;
            b[i-1]+=tmp;
            b[i-2]+=tmp+tmp;
        }
        else {
            int tmp=b[i]+a[i];
            tmp-=x;
            if(tmp<0)return 0;
            tmp/=3;
            b[i-1]+=tmp;
            b[i-2]+=tmp+tmp;
            b[i]=x;
        }
    }
    b[1]+=a[1];
    b[2]+=a[2];
    for(int i=1;i<=n;i++){
        if(b[i]<x)return 0;
    }
    return 1;
}
void vision()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    int l=1,r=1e9;
    while(l<=r){
        int mid=(l+r)/2;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    cout<<r<<"\n";
    return ;
}
signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--){
        vision();
    }
    return 0;
}

C 屠龙勇士

思路

       题意读明白了话其实很简单,就是一个判断点在直线的哪侧,给你三个点,判断第三个点在在前两个点所构成的直线的哪侧,这里我们用向量叉积来判断(这好像是高中的知识吧)

代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
	double x1,y1,x2,y2,x3,y3;
	cin>>x1>>y1>>x2>>y2>>x3>>y3;
	if((y2-y1)*(x3-x2)==(y3-y2)*(x2-x1))
	{
		printf("TOWARDS\n");
	}else if((y2-y1)*(x3-x2)>(y3-y2)*(x2-x1))
	{
		printf("RIGHT\n");
	}else{
		printf("LEFT\n");
	}
	return 0;
}

D 一道好玩的算法题

思路

        就按照题意模拟就可以了,这题主要是看之前讲的有没有都掌握。

代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int pr[N];
int xpr[N];
int n;
int q;
struct vis{
    int x,id;
};
vis a[N];
int cmp(vis x,vis y){
    if(x.x==y.x)return x.id<y.id;
    return x.x<y.x;
}
void vision(){
    cin>>n;
    cin>>q;
    for(int i=1;i<=n;i++){
        cin>>a[i].x;
        a[i].id=i;
        pr[i]=pr[i-1]+a[i].x;
        xpr[i]=xpr[i-1]^a[i].x;
    }
    sort(a+1,a+1+n,cmp);
    while(q--){
        int op;
        cin>>op;
        if(op==1){
            int x,y;
            cin>>x>>y;
            cout<<pr[y]-pr[x-1]<<"\n";
        }
        else if(op==2){
            int key;
            cin>>key;
            int l=0,r=n+1;
            while(l+1<r){
                int mid=(l+r)/2;
                if(a[mid].x>=key)r=mid;
                else l=mid;
            }
            cout<<a[r].id<<"\n";
        }
        else if(op==3){
            int x,y;
            cin>>x>>y;
            cout<<(xpr[y]^xpr[x-1])<<"\n";
        }
        else if(op==4){
            cout<<"haowan\n";
        }
    }
}
signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t=1;
//     cin>>t;
    while(t--){
        vision();
    }
    return 0;
}

E 圣遗物计算器

思路

        想出一道dfs但是没有好的想法,然后随便出了一题虽然五层循环暴力也能过,算考验代码能力吧。就正常的每种都跑循环然后判断就可以

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
struct vis{
    int a,b,c,d;
};
vis w[10][100];
int cnt[10];
int asa,asb,asc,asd;
int p=0;
void dfs(int ww,int ta,int tb,int tc,int td){
    if(ww==6){
        
        if(ta>=asa&&tb>=asb&&tc>=asc&&td>=asd){
            p=1;
            return ;
        }
        return ;
    }
    for(int i=1;i<=cnt[ww];i++){
        dfs(ww+1,ta+w[ww][i].a,tb+w[ww][i].b,tc+w[ww][i].c,td+w[ww][i].d);
        if(p==1)return ;
    }
}
void vision()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int tmp;
        cin>>tmp;
        cnt[tmp]++;
        cin>>w[tmp][cnt[tmp]].a>>w[tmp][cnt[tmp]].b>>w[tmp][cnt[tmp]].c>>w[tmp][cnt[tmp]].d;
    }
    cin>>asa>>asb>>asc>>asd;
//     cout<<cnt[1]<<" "<<cnt[2]<<" "<<cnt[3]<<" "<<cnt[4]<<" ";
//     cout<<asa<<" "<<asb<<" "<<asc<<" "<<asd<<" ";
    dfs(1,0,0,0,0);
    if(p==1)cout<<"YES\n";
    else cout<<"NO\n";
    return ;
}
signed main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t=1;
    // cin>>t;
    while(t--){
        vision();
    }
    return 0;
}

F 千玺!打响指!

思路

       自己先意会吧

代码
 cin >> n;
    string s;
    cin >> s;
    int num0 = 0;
    for(auto c : s)
    {
        num0 += c == '0';
    }
    int num1 = n - num0;
    reverse(s.begin(), s.end());

    int now = 0;
    LL res = 0;
    int i = 0;
    while(i < n)
    {
        if(s[i] == '0')
        {
            res += i - now;
            now ++;
            cout << res << ' ';
        }
        i ++;
    }
    lop(j, now, n)
    {
        cout << -1 << ' ';
    }
    cout << el;

G 叶熊赛跑

思路

        

其实我们可以想到,如果我们想让最终获奖的人最多,那就是把所有有可能获奖的人都算上。

如果一个先出发的序号被后面的序号追上,没在后面出发的序号前面回来,那这个前面的序号一定没有获奖的可能。那么其它的序号,就有可能获奖。

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
typedef pair<int,int> PII;
const int N=998244353;
const int MX=0x3f3f3f3f3f3f3f3f; 
int n,m;
int b[1000005];
struct we{
    int x,id;
    bool operator <(const we &k)const{
        return x<k.x;
    }
}hh[1000005];
void icealsoheat(){
    cin>>n;
    int ans=0;
int id=1;
    for(int i=1;i<=n;i++){
        cin>>hh[i].x;
        hh[i].id=i;
    }
    sort(hh+1,hh+1+n);
    int maxx=0;
    for(int i=1;i<=n;i++){
        if(maxx<hh[i].id){
            ans++;
        }
        maxx=max(maxx,hh[i].id);
    }
    cout<<ans<<"\n";
    

}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie();
    cout.tie();
    int _yq;
    _yq=1;
    cin>>_yq;
    while(_yq--){
        icealsoheat();
        // else break;
    }
}

H 小博怡情

思路:

       

结论:放在次小的位置,能必胜。 证明:

对于当前位置,如果它的左边,没有存在比它小的元素,那么此时Bob无法移动,Bob胜利。

对于当前位置,如果它的左边,存在2个以上比它小的元素,那么Bob可以选择将木块放在次小的位置,使得Alice操作后只能放在最小的元素,导致Bob必胜。也就是说以ai为起点的最长上升子序列的长度为2时才满足。

代码

I wkw玩拼图

思路

        

这题的证明用到了鸽巢定理,笼统来说,如果数据范围小,我们的双重for循环就可以跑完;如果数据范围大,那么序列中出现的数越多,他为了不让答案出现,不能出现的数就越多,最后导致一定会出现能形成答案的数,所以不用跑完for循环便可以找到答案

代码

J 扫描线

思路

        通过观察可以发现矩形的长和宽都在1000以内

        所以考虑采用二维前缀和去维护这n个矩形

        每次询问的时候可以O(1)查询

        故总的时间复杂度为O(1000*1000*t+q);

        

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int,int>PII;
typedef pair<PII,int>PPI;
const int N = 1e6+ 5;
PII A[N];
int ans[1005][1005];
void solve()
{	
	
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		int l,r;
		cin>>l>>r;
		ans[l][r]+=l*r;
	}
	for(int i=1;i<=1000;i++)
	{
		for(int j=1;j<=1000;j++)
		{
			ans[i][j]+=ans[i][j-1]+ans[i-1][j]-ans[i-1][j-1];
		}
	}
	
	while(q--)
	{
		int l1,l2,r1,r2;
		cin>>l1>>r1>>l2>>r2;
		int an=ans[l2-1][r2-1]-ans[l1][r2-1]-ans[l2-1][r1]+ans[l1][r1];
		cout<<an<<"\n";
	}
	for(int i=1;i<=1000;i++)
	{
		for(int j=1;j<=1000;j++)
		{
			ans[i][j]=0;
		}
	}
}                     
signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

K 狼吃羊

思路

        从贪心的角度考虑,Wolf不会选择一个方向后是不会往回走的

        故就是求长度为k的区间和最大。

        采用前缀和维护然后遍历

        时间复杂度为O(n)

代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 4e5 + 5;
int A[N],sum[N];
void solve()
{
	
	int n,k;
	cin>>n>>k;
	
	for(int i=1;i<=n;i++)
	{
		cin>>A[i];
		sum[i]=sum[i-1]+A[i];
	}
	int ans=0;
	for(int i=k;i<=n;i++)
	{
		ans=max(ans,sum[i]-sum[i-k]);
	}
	cout<<ans<<"\n";
}
signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	int t=1;
//	cin>>t;
	while(t--)
	{
		solve();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值