Codeforces Round #756 (Div. 3) 个人题解

Codeforces Round #756 (Div. 3) 个人题解

纪念小飞龙CF上蓝!(关于为什么小飞龙这个菜逼打了这么多把才刚上蓝这件事)

比赛链接:Codeforces Round #756 (Div. 3)

A题 Make Even

题目大意:

给你一个不包含 \(0\) 的数,每一次操作你可以选择这个数的一个前缀翻转它,问最小操作次数能够使得这个数为偶数,不能则输出 \(-1\)

思路解析:

显然,如果一个数中每一位都是奇数,那么一定是 \(-1\)

如果这个数本身就是偶数,那么不需要操作,即为 \(0\)

否则,如果第一位是偶数的话我们可以将整个数翻转,答案即为 \(1\)

否则的话,我们需要多用一次操作把中间的一个偶数翻转到头上来,转化为上一种情况,答案为 \(2\)

AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;


int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        string s;
        cin>>s;
        int n=s.size();
        if((s[n-1]-'0')%2==0)cout<<0<<endl;
        else {
            int sum=0;
            for(int i=0;i<n;i++){
                int j=s[i]-'0';
                if(j%2==1)sum++;
            }
            if(sum==n){
                cout<<-1<<endl;
                continue;
            }
            if((s[0]-'0')%2==0)cout<<1<<endl;
            else cout<<2<<endl;
        }
    }
}

B题 Team Composition: Programmers and Mathematicians

题目大意:

我们有 \(a\) 个数学家和 \(b\) 个程序员,每个队四个人,但是不能全是数学家或者程序员,问最大的组队数量

思路解析:

简单的数学问题,显然答案一定是 \(min(min(a,b),(a+b)/4)\)

AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;



int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        ll a,b;
        cin>>a>>b;
        ll ans=min(min(a,b),(a+b)/4);
        cout<<ans<<endl;
    }
}

C题 Polycarp Recovers the Permutation

题目大意:

我们有一个数组 \(p\) 和一个数组 \(a\) ,一开始p是一个 \(1-n\) 的排列, \(a\) 是空的,我们每次把 \(p\) 两端小的那个元素弹出放到同侧的 \(a\) 当中,直到 \(p\) 中没有元素

现在给出了操作后的a数组,构造一个可行的 \(p\) ,如果不能输出 \(-1\)

思路解析:

我们首先考虑 \(-1\) 的情况,我们发现每一次都是取小的那一个,所以原排列中最后弹出的一定是 \(n\) ,所以在 \(a\) 中如果 \(n\) 在中间的话一定不可行

对于一个可行解,我们可以直接把 \(a\) 倒退,用 \(deque\) 模拟即可

AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

int a[maxn];

int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        deque<int>q;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        if(a[1]!=n&&a[n]!=n){
            cout<<-1<<endl;
            continue;
        }
        int l=1,r=n;
        while(l<=r){
            if(a[l]>a[r])q.push_front(a[l++]);
            else q.push_back(a[r--]);
        }
        while(q.size()){
            cout<<q.front()<<" ";
            q.pop_front();
        }
        cout<<endl;
    }
}

D题 Weights Assignment For Tree Edges

题目大意:

给出一颗树(有向),给出 \(p\) 数组,满足 \(dis[p[i]]<dis[p[i+1]]\)\(dis\) 表示到根节点的距离)

你需要构造一棵树,输出每条边的权值(即当前点到其父亲节点的距离),如果不能输出 \(-1\)

思路解析:

对于这道题,小飞龙的思路很简单粗暴,核心就是判断冲突

我们直接构造一个 \(dis[p[i]]=dis[p[i-1]]+1\) 人,然后在树上 \(dfs\) ,找冲突,即当前点 \(u\) 的子树中有存在一个点 \(v\) 满足 \(dis[v]<dis[u]\) ,如果存在这样的情况,答案一定是 \(-1\) ,否则我们当前的构造就是可行的,输出 \(dis[i]-dis[fa[i]]\) 即可

(附一份我队友zc聚聚写得简介思路,直接判断)

我的AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

int a[maxn],p[maxn];
ll dis[maxn];
int e[maxn],nex[maxn],h[maxn],id;
int rt,ok,q[maxn];

void add(int x,int y){
    e[++id]=y;
    nex[id]=h[x];
    h[x]=id;
}

void dfs(int x){
    q[x]=dis[x];
    for(int i=h[x];i;i=nex[i]){
        int j=e[i];
        dfs(j);
        q[x]=min(q[x],q[j]);
    }
    if(q[x]<dis[x])ok=0;
}


int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n+5;i++)h[i]=0,dis[i]=0,q[i]=0;
        id=0;
        rt=0;ok=1;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(a[i]==i){
                rt=i;
                continue;
            }
            add(a[i],i);
        }
        for(int i=1;i<=n;i++){
            cin>>p[i];
            dis[p[i]]=i-1;
        }
        dfs(rt);
        if(!ok){
            cout<<-1<<endl;
            continue;
        }
        for(int i=1;i<=n;i++){
			cout<<dis[i]-dis[a[i]]<<" ";
		}
        cout<<endl;
    }
}

zc聚聚的思路

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

int a[maxn],p[maxn];
ll dis[maxn];
int e[maxn],nex[maxn],h[maxn],id;
int rt,ok,q[maxn],in[maxn];

void add(int x,int y){
    e[++id]=y;
    nex[id]=h[x];
    h[x]=id;
}

void dfs(int x){
    q[x]=dis[x];
    for(int i=h[x];i;i=nex[i]){
        int j=e[i];
        dfs(j);
        q[x]=min(q[x],q[j]);
    }
    if(q[x]<dis[x])ok=0;
}

int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n+5;i++)dis[i]=-1;
        ok=1;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
            cin>>p[i];
        for(int i=1;i<=n;i++){
            dis[p[i]]=i-1;
            if(dis[a[p[i]]]<0){
                cout<<-1<<endl;
                ok=0;
                break;
            }
        }
        if(ok==0)continue;
        for(int i=1;i<=n;i++){
            cout<<dis[i]-dis[a[i]]<<" ";
        }
        cout<<endl;
    }
}

E1题 Escape The Maze (easy version

题目大意:

有一颗树,小 \(V\)\(1\) 号点,他有 \(k\) 个朋友分别在 \(x_i\) 号点,每一秒他们移动一步,小 \(V\) 成功的条件是他可以走到任意一个叶节点,如果他在途中被他的朋友抓到,那么他就输了,问小 \(V\) 是否能成功

思路解析:

显然我们可以用两次 \(bfs\) 来解决这道问题:

第一次 \(bfs\) 我们处理出朋友们到达每个点的最短时间,假设为 \(dis[i]\)

第二次 \(bfs\) 我们处理小 \(V\) 到达每个点的时间,我们假设为 \(d[i]\) ,如果在某个点 \(x\)\(d[x]>=dis[x]\) ,那么就说明如果小 \(V\) 走这个点一定会被抓到,显然我们就可以在 \(bfs\) 中剪掉这一枝,剪掉 \(x\) 的子树

那么小 \(V\) 胜利的条件就是在 \(bfs\) 中能到达任何一个叶节点,然后判断即可

AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

int e[maxn],nex[maxn],h[maxn],id;
int pos[maxn],dis[maxn],d[maxn],in[maxn];

void add(int x,int y){
    e[++id]=y;
    nex[id]=h[x];
    h[x]=id;
}

int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n+10;i++){
            h[i]=0;dis[i]=-1;d[i]=-1;
            in[i]=0;
        }
        id=0;
        queue<int>q;
        for(int i=1;i<=m;i++){
            int x;
            cin>>x;
            dis[x]=0;
            q.push(x);
        }
        for(int i=1;i<n;i++){
            int x,y;
            cin>>x>>y;
            add(x,y);
            add(y,x);
            in[x]++;
            in[y]++;
        }
        while(q.size()){
            int top=q.front();
            q.pop();
            for(int i=h[top];i;i=nex[i]){
                int j=e[i];
                if(dis[j]==-1){
                    dis[j]=dis[top]+1;
                    q.push(j);
                }
            }
        }
        queue<int>qq;
        qq.push(1);
        d[1]=0;
        int ok=0;
        while(qq.size()){
            int top=qq.front();
            qq.pop();
            if(in[top]==1&&top!=1)ok=1;
            for(int i=h[top];i;i=nex[i]){
                int j=e[i];
                if(d[j]==-1){
                    d[j]=d[top]+1;
                    if(dis[j]<=d[j])continue;
                    qq.push(j);
                }
            }
        }
        if(ok)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

E2题 Escape The Maze (hard version)

题目大意:

\(E1\) 基本相同,只是你需要输出在小 \(V\) 失败的情况下,能够抓到小 \(V\) 所需的最小人数

思路解析:

有了上一题的基础,我们会发现我们在第一次 \(bfs\) 中每剪掉一颗子树,就需要一个人,此时必要人数 \(tot++\)

\(tot\) 即为答案,输出 \(tot\) 即可

AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

int e[maxn],nex[maxn],h[maxn],id;
int pos[maxn],dis[maxn],d[maxn],in[maxn];

void add(int x,int y){
    e[++id]=y;
    nex[id]=h[x];
    h[x]=id;
}

int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n+10;i++){
            h[i]=0;dis[i]=-1;d[i]=-1;
            in[i]=0;
        }
        id=0;
        queue<int>q;
        for(int i=1;i<=m;i++){
            int x;
            cin>>x;
            dis[x]=0;
            q.push(x);
        }
        for(int i=1;i<n;i++){
            int x,y;
            cin>>x>>y;
            add(x,y);
            add(y,x);
            in[x]++;
            in[y]++;
        }
		int sum=0;
		for(int i=1;i<=n;i++)if(in[i]==1)sum++;
        while(q.size()){
            int top=q.front();
            q.pop();
            for(int i=h[top];i;i=nex[i]){
                int j=e[i];
                if(dis[j]==-1){
                    dis[j]=dis[top]+1;
                    q.push(j);
                }
            }
        }
        queue<int>qq;
        qq.push(1);
        d[1]=0;
        int ok=0,tot=0;
        while(qq.size()){
            int top=qq.front();
            qq.pop();
            if(in[top]==1&&top!=1)sum--,ok=1;
            for(int i=h[top];i;i=nex[i]){
                int j=e[i];
                if(d[j]==-1){
                    d[j]=d[top]+1;
                    if(dis[j]<=d[j]){
						tot++;
						continue;
					}
                    qq.push(j);
                }
            }
        }
		if(ok)cout<<-1<<endl;
		else cout<<tot<<endl;
    }
}

F题 ATM and Students

题目大意:

给出一台 \(ATM\) 机,一开始里面有 \(s\) 块钱,有 \(n\) 个人存钱或者取钱,问最大的连续子段使得能够满足子段中每个人的需求(即不会出现取钱钱不够的情况)

思路解析:

尺取,维护当前区间和 \(+s>=0\) 即可

(附一份队友 \(tbw\) 聚聚 的 **\(ST\) 表+二分代码 (经典赛后过题))

我的AC代码:

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

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n'
#define pii pair<int,int>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int maxn=1e6+5;

ll n,a[maxn],s,sum[maxn];

int main(){
    IOS
    int t;
    cin>>t;
    while(t--){
        cin>>n>>s;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
        ll now=0;
        ll l=1,r=1,x=0,y=0,sz=-1;
        while(l<=r&&r<=n){
            now+=a[r++];
            if(now+s>=0&&r>sz+l)sz=r-l,x=l,y=r;
            while(now<-s)now-=a[l++];
        }
        if(sz==-1)cout<<sz<<endl;
        else cout<<x<<" "<<y-1<<endl;
    }
}

tbw聚聚的代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
const int N=2e5+10,M=18;

int n,s;
int a[N];
int b[N];

int f[N][M];

void init()
{
    for(int j=0;j<M;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            if(!j) f[i][j]=b[i];
            else f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
vector<int> ans;

int query(int l,int r)
{
    int len=r-l+1;
    int k=log(len)/log(2);
    return min(f[l][k],f[r+1-(1<<k)][k]);
}

bool check(int mid){
	for(int i=1;i<=n;i++){
	    if(i+mid-1<=n){
			int t=query(i,i+mid-1)-b[i-1];
			if(t<0&&s>=(-t)) return true;
			else if(t>=0) return true;
	    }
	}
	return false;
}

void solve(){
	vector<int> ans;
	//ans.clear();
	cin>>n>>s;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=b[i-1]+a[i];
	}
	init();
	int l=0,r=n;
	while(l<r){
		int mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
    if(!r){
        cout<<"-1\n";
        return;
    }
    for(int i=1;i<=n;i++){
        if(i+r-1<=n){
			int t=query(i,i+r-1)-b[i-1];
			if(t<0&&s>=(-t)||t>=0){
			    cout<<i<<' '<<i+r-1<<endl;
			    return;
			}
        }
    }
}

signed main(){
	int T;cin>>T;while(T--)
	solve();
	return 0;
}

推广一波小飞龙博客:戳这里@不会飞的小飞龙

posted @ 2021-11-26 16:53  不会飞的小飞龙  阅读(54)  评论(0编辑  收藏  举报
Live2D