寒假训练第一周

寒假训练第一周

第一天

蓝桥个人赛

A.带分数

大意:
求数字n有多少种带分数表示方法(每种表示由1~9不重不漏组成)

思路:
暴力搜索
全排列函数next_permutation()
外层while循环是全排列,
内层for循环分配数字给整数部分和分数部分。

代码:


#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'int a[10];
int work(int l,int r){
    int sum=0;
    for(int i=l;i<=r;i++){
        sum=sum*10+a[i];
    }
    return sum;
}//验证int t,n;
int main(){
    for(int i=0;i<10;i++) a[i]=i+1;
    cin>>n;
    int ans=0;
    do{
        for(int i=0;i<7;i++){
            int x=work(0,i);
            if(x>n) break;
            for(int j=i+1;j<8;j++){
                int y=work(i+1,j),z=work(j+1,8);
                if(y%z==0&&x+y/z==n){
                    ans++;
                }
            }
        }
    }while(next_permutation(a,a+9));
    cout<<ans;
    return 0;
}

D.灵能传输

大意:
(ai−1​,ai​,ai+1​)←(ai−1​+ai​,−ai​,ai+1​+ai​)
通过不限次数的操作使ai的最大绝对值最小

思路:
把前缀和用到极致
三个数一次变换后相当于前两个数的前缀和交换,也就是除最后一个数,其他数可以任意排位
加上s[0]=0
按0最小最大~最后一个数的顺序排列
隔一个数依次排

代码:


#include <bits/stdc++.h>using namespace std;
#define endl '\n'#define ll long longint main(){
    ll t;
    cin>>t;
    while(t--) {
        ll n;
        cin>>n;
        vector<ll> pre(n + 1);
        for (int i = 1; i <= n; i++) {
            cin >> pre[i];
            pre[i] += pre[i - 1];
        }
        ll L = pre[0], R = pre.back();
        if (L > R) swap(L, R);
        sort(pre.begin(), pre.end());
        int l = lower_bound(pre.begin(), pre.end(), L) - pre.begin();
        int r = lower_bound(pre.begin() + l + 1, pre.end(), R) - pre.begin();
        vector<ll> ve, vr;
        vector<bool> vis(n + 1);
        for (int i = l; i >= 0; i -= 2) {
            ve.push_back(pre[i]);
            vis[i] = 1;
        }
        for (int i = r; i <= n; i += 2) {
            vr.push_back(pre[i]);
            vis[i] = 1;
        }
        reverse(vr.begin(), vr.end());
        for (int i = 0; i <= n; i++) {
            if (!vis[i]) ve.push_back(pre[i]);
        }
        for (ll i: vr) ve.push_back(i);
        ll ans = 0;
        for (int i = 0; i < n; i++) {
            ans = max(ans, abs(ve[i] - ve[i + 1]));
        }
        cout << ans << endl;
    }
    return 0;
}

E.后缀表达式

大意:
给定N个加号,M个减号以及N+M+1个整数,求凑出的合法后缀表达式中最大的结果

思路:
后缀表达式就是可以任意加括号的意思
一、没有减号全加起来
二、1.有减号且全是正数或全是负数,减去一个绝对值最小值
2.有减号且有正有负,所有绝对值相加

代码:


#include <bits/stdc++.h>using namespace std;
#define endl '\n'#define ll long longconst int N=2e5+10;
ll a[N];
int main(){
    int n,m;
    int ans=0;
    ll sum=0,num=0;
    cin>>n>>m;
    for(int i=1;i<=n+m+1;i++){
        cin>>a[i];
        if(a[i]<0) ans++;
        sum+=abs(a[i]);
        num+=a[i];
    }
    if(m==0) cout<<num<<endl;
    else{
        sort(a+1,a+m+n+2);
        if(ans==0) sum-=2*a[1];
        else if(ans==n+m+1) sum+=2*a[n+m+1];
        cout<<sum<<endl;
    }
    return 0;
}

第二天

cf Hello 2023!

B. MKnez's ConstructiveForces Task

大意:
题目大意:给出一整数n,要求输出一个长为n的数组,使得任意两个相邻数的和都等于所有数的和,且数组中不能出现0

思路:
写出来找规律

代码:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
    ll t;
    cin>>t;
    while(t--) {
        int n;
        cin >> n;
        if (n == 3)
        {
            cout << "NO" << endl;
        }
        else if (n & 1)
        {
            cout << "YES" << endl;
            for (int i = 1; i <= n - 1; i += 2)
            {
                cout << (n - 1) / 2 - 1 << " " << -(n - 1) / 2 << " ";
            }
            cout << (n - 1) / 2 - 1 << endl;
        }
        else
        {
            cout << "YES" << endl;
            for (int i = 1; i <= n; i += 2)
            {
                cout << "1 -1 ";
            }
            cout << endl;
        }

    }
    return 0;
}

C. Least Prefix Sum

大意:
给定一个数组,可以对数进行取反操作,先要让s[m]最小,求最少操作次数

思路:
根据前缀和性质
分m后面的数有负数和m前的数有正数的情况去讨论

代码:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
    ll t;
    cin>>t;
    while(t--) {
        ll n,m;
        cin>>n>>m;
        ll ans=0;
        vector<ll> ve(n+1),pre(n+1);
        ll sum=0,suf=0;
        for(ll i=1;i<=n;i++){
            cin>>ve[i];
            pre[i]=pre[i-1]+ve[i];
        }
        priority_queue<ll> q;
        for(ll i=m+1;i<=n;i++){
            if(ve[i]<0) q.push(-ve[i]);
            while(pre[i]+suf<pre[m]){
                suf+=q.top()*2;
                q.pop();
                ans++;
            }
        }
        while(q.size()) q.pop();
        for(ll i=m;i>=1;i--){
            if(pre[i]+sum<pre[m]){
                sum+=2*q.top();
                q.pop();
                ans++;
            }
            if(ve[i]>0) q.push(ve[i]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

第三天

中文个人赛

E.狠狠地切割(hard version)

大意:
一个序列设置多个断点,问序列被切割了多少段

思路:
二分查找是否存在断点
一个数不是断点而后一个数是断点则序列+1
注意特判最后一个数,没被切割序列+1

代码:


#include <bits/stdc++.h>using namespace std;
#define ll long longconst ll N=5e5+5;
#define endl '\n'
ll n,m,ans;
ll a[N],b[N];
bool check(ll k){
    ll l=1,r=m,mid;
    while(l<=r){
        mid=(l+r)/2;
        if(k<b[mid]) r=mid-1;
        else if(k>b[mid]) l=mid+1;
        else return 1;
    }
    return 0;
}
int main(){
    cin>>n>>m;
    for(ll i=1;i<=n;i++){
        cin>>a[i];
    }
    for(ll i=1;i<=m;i++){
        cin>>b[i];
    }
    sort(b+1,b+1+m);
    if(!check(a[n])) ans++;
    for(ll i=1;i<n;i++){
        if(!check(a[i])&&check(a[i+1])) ans++;
    }
    cout<<ans;
    return 0;
}

G.如何得到npy

大意:
给定一棵 n 个节点的无根树和两个关键点 s,t,
要求对所有边定向,
并计算出每个点到较近的关键点的最小和

思路:
从s和t出发各跑一遍dfs记录到每个点的距离,
最后答案距离取两者中小的那个
输出若两个端点去到同一个终点,则距离较大的立标牌
若去到不同终点,则无需立标牌

代码:


#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'const ll N=3e5+5;
vector<pair<ll,ll>> g[N];
ll dis1[N],dis2[N],ansdis[N];
vector<pair<ll,ll>> edge;
void dfs(ll u,ll fa,ll dis[]){
    for(auto t:g[u]){
        ll v=t.first,w=t.second;
        if(v==fa) continue;
        dis[v]=dis[u]+w;
        dfs(v,u,dis);
    }
}
int main(){
    ll n,s,t;
    cin>>n>>s>>t;
    ll x,y,z;
    for(ll i=1;i<=n-1;i++){
        cin>>x>>y>>z;
        g[x].push_back({y,z});
        g[y].push_back({x,z});
        edge.push_back({x,y});
    }
    dfs(s,0,dis1);
    dfs(t,0,dis2);
    ll sum=0;
    for(ll i=1;i<=n;i++){
        ansdis[i]=min(dis1[i],dis2[i]);
        sum+=ansdis[i];
    }
    cout<<sum<<endl;
    for(ll i=0;i<n-1;i++){
        ll u=edge[i].first,v=edge[i].second;
        if(dis1[u]==ansdis[u]&&dis1[v]==ansdis[v]
        ||dis2[u]==ansdis[u]&&dis2[v]==ansdis[v]){
            if(ansdis[u]<ansdis[v]) cout<<2;
            else if(ansdis[u]==ansdis[v]) cout<<0;
            else cout<<1;
        }else cout<<0;
    }
    return 0;
}

H.做不完的作业

大意:
n个任务有n个所需时间,需依次完成,完成一个任务所需时间连续,每天必须休息,前i天休息时间总和不少于r乘x乘i,求至少需要多少天

思路:
一天时间 − 将要完成的作业时间 + 当前总睡觉时间 +l 天整天睡觉的总时长 ≥ 当前天的 l 天后的要求总睡觉时长
把式子化简,避免出现除法

代码:


#include <bits/stdc++.h>using namespace std;
#define ll long long#define endl '\n'const ll N=3e5+5;
int main(){
    ll n,x,p,q;
    ll i=1,sum=0,t=0;
    cin>>n>>x>>p>>q;
    while(n--){
        ll w;
        cin>>w;
        if((x-t-w+sum)*q>=i*p*x&&x-t>w) t+=w;
        else{
            sum+=x-t;
            i++;
            long long l=ceil((q*(sum+x-w)-p*i*x)*1.0/(x*p-x*q));
            if(l>0){
                sum+=x*l;
                i+=l;
            }
            t=w;
        }
    }
    cout<<i;
    return 0;
}

第四天

cf ROUND#842(Div.2)

C. Elemental Decompress

大意:
给定一个长度为n的数组a,让构造出两个排列p、q,使得对于数组a的每个元素a[i],让max(p[i],q[i])=a[i],可以输出YES,并输出p和q,否则输出NO

思路:
记录下原数组的数和位置
先排大数
数字出现2次以上buhef
出现2次,让两个排列的不同位置填它,并分别记录下空出的位置
出现1次,让两个排列的相同位置填它
对于未出现的数,塞到空出的位置

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int n,T,a[N];
vector<int>v[N];
int p[N],q[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++)cin>>a[i],v[a[i]].push_back(i);
		vector<int>P,Q;
		bool fail=0;
		for(int i=n;i;i--){
			if(v[i].size()>2){
				fail=1;
				break;
			}
			if(v[i].size()==2){
				p[v[i][0]]=q[v[i][1]]=i;
				P.push_back(v[i][0]),Q.push_back(v[i][1]);
			}
			else if(v[i].size()==1){
				p[v[i][0]]=q[v[i][0]]=i;
			}
			else{
				if(!P.size()||!Q.size()){
					fail=1;
					break;
				}
				p[Q.back()]=q[P.back()]=i;
				P.pop_back(),Q.pop_back();
			}
		}
		if(fail)cout<<"NO\n";
		else{
			cout<<"YES\n";
			for(int i=1;i<=n;i++)cout<<p[i]<<' ';
			cout<<'\n';
			for(int i=1;i<=n;i++)cout<<q[i]<<' ';
			cout<<'\n';
		}
		for(int i=1;i<=n;i++)v[i].clear();
	}
}

第五天

英文个人赛

G.Quests

大意:
有一定量的任务和所对应的奖励,要求在d天内至少拿到c的奖励,问相同任务最多能隔几天再做

思路:
二分答案

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=2e5+5;
ll a[N];
int main()
{
    ll t;
    cin>>t;
    while(t--) {
        ll n,c,d;
        cin>>n>>c>>d;
        for(ll i=1;i<=n;i++){
            cin>>a[i];
        }
        sort(a+1,a+1+n);
        if(a[n]*d<c){
            cout<<"Impossible"<<endl;
            continue;
        }
        ll res=0;
        for(ll i=1;i<=min(n,d);i++){
            res+=a[n-i+1];
            if(res>=c) break;
        }
        if(res>=c){
            cout<<"Infinity"<<endl;
            continue;
        }
        ll l=0,r=d+10;
        while(l<r){
            ll day=0;
            ll mid=(l+r+1)/2;
            res=0;
            ll sum=0;
            for(ll i=n;i>=1;i--){
                res+=d/(mid+1)*a[i];
                day+=d/(mid+1);
                if((d%(mid+1))-sum>0){
                    res+=a[i];
                    day++;
                }
                if(day==d) break;
                sum++;
            }
            if(res>=c) l=mid;
            else if(res<c) r=mid-1;
        }
        cout<<l<<endl;
    }
    return 0;
}

H.SlavicG's Favorite Problem

大意:
一棵树,给定起点终点,每条边上有数值,有一次到任一点(b除外)的传送机会,要求到b时数值为0,对所有走过的边的数值求异或和

思路:
分别从起点和终点跑一遍dfs,记录下到每个点的异或和,若出现相同数值,则成功

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 200005;
int n, m, a, b;
int h[maxn], e[maxn], ne[maxn], w[maxn], index;
int ans;
bool flag;
map<int, int> mp;

void add(int x, int y, int z) {
    e[index] = y;
    w[index] = z;
    ne[index] = h[x];
    h[x] = index;
    index++;
}

void dfsa(int x, int y, int z) {//从起点a开始dfs
    mp[z] = 1;//记录每次异或运算的结果,第一次是0
    for (int i = h[x]; ~i; i = ne[i]) {
        if (e[i] == b || e[i] == y)
            continue;
        dfsa(e[i], x, z ^ w[i]);
    }
}

void dfsb(int x, int y, int z) {//从终点b开始dfs
    for (int i = h[x]; ~i ; i = ne[i]) {
        if (e[i] == y)
            continue;

        if (mp[z ^ w[i]]) {
            flag = true;
            
            return;
        }
        dfsb(e[i], x, z ^ w[i]);
    }
}


int main() {
    int t;
    cin >> t;
    while (t--) {
        index = 0;
        mp.clear();

        cin >> n >> a >> b;
        memset(h, -1, sizeof(h));
        for (int i = 0; i < n - 1; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            add(u, v, w);
            add(v, u, w);
        }

        dfsa(a, 0, 0);

        flag = false;
        dfsb(b, 0, 0);
        if (!flag)
            cout << "NO" << endl;
        else cout << "YES" << endl;
        //if (dfsb(b, 0, 0))
        //	cout << "YES" << endl;
        //else
        //	cout << "NO" << endl;
    }
}

I.The Humanoid

大意:给定初始战斗值,2次战斗值乘2的机会,1次战斗值乘3的机会,当战斗值大于敌人时将敌人战斗值除二加到自己的战斗值上,问最多干掉几个敌人

思路:枚举

代码:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
const ll N=2e5+10;
ll a[N];
int main()
{
    ll t;
    cin>>t;
    while(t--){
        ll n,h;
        cin>>n>>h;
        for(ll i=0;i<n;i++){
            cin>>a[i];
        }
        sort(a,a+n);
        ll ans=0;
        for(ll k=0;k<=2;k++){
            ll up=0;
            ll i=0;
            ll tem=h;
            while(i<n){
                if(tem>a[i]) tem+=a[i++]/2;
                else if(up==3) break;
                else{
                    if(up==k) tem*=3;
                    else tem*=2;
                    up++;
                }
            }
            ans=max(ans,i);
        }
        cout<<ans<<endl;
    }
    return 0;
}

J. Make It Round

大意:
给定数n和可乘的倍数,求在倍数内最大且后缀0最多的数

思路:
2乘5为10,1乘10为10,
尽量把2,5凑对,
再凑10

代码:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
const ll N=2e5+10;
int main()
{
    ll t;
    cin>>t;
    while(t--){
        ll n,m;
        cin>>n>>m;
        ll cnt2=0,cnt5=0;
        ll tem=n;
        while(tem%2==0){
            cnt2++; tem/=2;
        }
        tem=n;
        while(tem%5==0){
            cnt5++; tem/=5;
        }
        ll x=1;
        while(cnt5>cnt2&&x*2<=m){
            cnt5--; x*=2;
        }
        while(cnt5<cnt2&&x*5<=m){
            cnt2--; x*=5;
        }
        while(x*10<=m) x*=10;
        cout<<m/x*x*n<<endl;
    }
    return 0;
}

K. Thermostat

大意:
给定初始温度、温度范围、最少变化量和目标值,
问最少需操作几次达到目标值

思路:
直接考虑最远的边界

代码:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
int main()
{
    ll t;
    cin>>t;
    while(t--) {
        ll l, r, x, a, b;
        cin >> l >> r >> x >> a >> b;
        if (a == b){
            cout<<0<<endl;
            continue;
        }else if(abs(a-b)>=x){
            cout<<1<<endl;
            continue;
        }else if(r-max(a,b)>=x||min(a,b)-l>=x){
            cout<<2<<endl;
            continue;
        }else if(r-b>=x&&a-l>=x||r-a>=x&&b-l>=x){
            cout<<3<<endl;
            continue;
        }else cout<<-1<<endl;
    }
    return 0;
}

第六天

牛客小白月赛65

C.牛牛排队五

大意:
n个人,抽走一些人,问指定人前面一人的序号

思路:
set容器、find()、erase()、prev()

代码:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,k,choice,num;
	cin>>n>>k;
	vector<int> pre(n+2),nxt(n+2);
	for(int i=1;i<=n;i++)
	{
		pre[i]=i-1;
		nxt[i]=i+1;
	}
	while(k--)
	{
		cin>>choice>>num;
		if(choice==1)
		{
			nxt[pre[num]]=nxt[num];
			pre[nxt[num]]=pre[num];
		}
		else
		cout<<pre[num]<<endl;
	}
	return 0;
}
posted @ 2023-01-08 18:29  WW爆米花  阅读(19)  评论(0编辑  收藏  举报