Ethflow Round 1 (Codeforces Round 1001, Div. 1 + Div. 2)(A-E1)

题目链接:Dashboard - Ethflow Round 1 (Codeforces Round 1001, Div. 1 + Div. 2) - Codeforces

A. String

思路

可以发现最小反转次数就是把每个1单独反转为0就行,即统计1的个数

代码

void solve(){
    string s;
    cin>>s;
    int sum=0;
    for(int i=0;i<s.size();i++){
        sum+=(s[i]-'0');
    }
    cout<<sum<<"\n";
}

B. Clockwork

思路

我们要保证每个i都要到达因为要重置,对于i节点我们要从i-n-i或从i-1-i,只要a[i]>=max((n-i)*2,(i-1)*2)即可满足无限循环

代码

void solve(){
    int n;
    cin>>n;
    vi a(n+10);
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    bool f=true;
    for(int i=1;i<=n/2;i++){
        if(a[i]<=(n-i)*2) f=false;
    }
    for(int i=n/2;i<=n;i++){
        if(a[i]<=(i-1)*2) f=false;
    }
    if(f){
        cout<<"YES\n";
    }else{
        cout<<"NO\n";
    }
}

C. Cirno and Operations

思路

操作1为反转,操作2为差序列替换,我们可以发现在进行操作2之后和就变成了a[n]-a[1],再进行操作1之后和为a[1]-a[n],所以只要把原始序列的和拿出来,之后操作1改变的只是答案的正负号,把所有的可能取绝对值求最大即可,注意要把原始序列单独拿出来因为还没有进行操作2所以和不能按绝对值来算

代码

void solve(){
    int n;
    cin>>n;
    vi a(n+10);
    int x=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        x+=a[i];        
    }
    if(n==1){
        cout<<a[1]<<"\n";return;
    }
    int t=n;
    while(t){
        int sum=0;
        sum=a[t]-a[1];
        x=max(abs(sum),x);
        t--;
        for(int i=1;i<=t;i++){
            a[i]=a[i+1]-a[i];
        }
    }
    cout<<x<<"\n";
}

D. Balanced Tree

思路

D这题也是挺有意思的

首先我们在思考这个题的时候发现a[i]的具体值是难以确定的,我们不妨先假设a的值已经全部确定了,要使它全部变成相同的数,那么唯一有用的操作便是对于x与y两个相连的顶点,让其u=x,v=y或u=y,v=x不断让相连的两顶点相等,因为是v的子树都+1,所以相连的点只有x与y的差值会减少1,其他相连点的差值不会改变,这样就能保证我们最终能得到全是相同的数,并且是最小的

然后我们假设我们从1开始遍历,答案就是a[1]+\sum max(a[v]-a[u],0) v是孩子节点,u是父节点

那么剩下的任务就是如何确定a的值让a[1]+\sum max(a[v]-a[u],0)最小,对于叶子节点我们让其最小即可,对于有子节点的我们让其等于min(r[u],max(a[v]))即最接近子节点的最大值,这样就能够保证答案最小化

代码

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

#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); 
#define int long long
#define ull unsigned long long
#define bit __builtin_popcount
#define lowbit(x) ((x)&-(x))
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;

const int N=2e5+10;
const int inf=1e18;
const int mod=998244353;

void solve(){
	int n;
	cin>>n;
	vector<int> l(n+1),r(n+1),a(n+1);
	for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
	vector<vi> e(n+1);
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}	
	int ans=0;
	function<void(int,int)> dfs=[&](int u,int fa){
		int mx=l[u];
		for(int v:e[u]){
			if(v==fa) continue;
			dfs(v,u);
			mx=max(mx,a[v]);
		}
		a[u]=min(mx,r[u]);
		for(int v:e[u]) ans+=max(0ll,a[v]-a[u]);
	};
	dfs(1,-1);
	cout<<ans+a[1]<<"\n";
}
signed main() {
	vcoistnt
	int _=1;
	cin>>_;
	while(_--) solve();
	return 0;
}

E1. The Game (Easy Version)

思路

首先这是一道博弈题,发现Cirno可能选择的节点必须满足以下两个条件:

1.Cirno选择的节点u,存在一个节点v,v不在u的子树里面并且w_{v}>w_{u}

2.这样的节点v只能存在1个,意思是当Daiyousei选择节点v之后并把v的子树给删掉,Cirno没有其他节点可以选择了,这样Cirno就胜利了

关于第一个条件。我们可以用dfn序维护前后缀来实现,具体来说,dfn序就是把树的节点按DFS的顺序放在一条链上进行处理,我们用dfn[i]表示节点i第一次出现的位置,low[i]表示dfs搜索完节点i出来的节点,即[dfn[i],low[i]]表示节点i及其子树所在的区间,我们用pre[i]表示前缀最大值,suf[i]表示后缀最大值,那么遍历到i节点时,只要满足max(pre[dfn[i]-1],suf[low[i]+1])>w[i]就说明存在一个节点v,v不在u的子树里面并且w_{v}>w_{u}

关于第二个条件。我们要贪心地去想,我们只要选择满足条件1的最大的w的节点即可,这样就一定会满足条件二

代码

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

#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); 
#define int long long
#define ull unsigned long long
#define bit __builtin_popcount
#define lowbit(x) ((x)&-(x))
#define vi vector<int>
#define vb vector<bool>
typedef pair<int,int> pll;

const int N=1e6+10;
const int inf=1e18;
const int mod=998244353;

int n,id;
int w[N],dfn[N],nfd[N],low[N];
vb vis(N);
vector<int> e[N];

void dfs(int u){
    if(vis[u]) return;
    vis[u]=true;
    dfn[u]=++id;
    nfd[id]=u;
    for(auto v:e[u]) dfs(v);
    low[u]=id;
    vis[u]=false;
}

void solve(){

    id=0;
    for(int i=1;i<=n;i++){
        e[i].clear();
    }

    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1);
    vi pre(n+10),suf(n+10);
    for(int i=1;i<=n;i++){
        pre[i]=max(pre[i-1],w[nfd[i]]);
    }
    for(int i=n;i>=1;i--){
        suf[i]=max(suf[i+1],w[nfd[i]]);
    }
    int mx=0;
    for(int i=1;i<=n;i++){
        if(max(pre[dfn[i]-1],suf[low[i]+1])>w[i]&&w[i]>w[mx]){
            mx=i;
        }
    }
    cout<<mx<<"\n";
}
signed main() {
    vcoistnt
    int _=1;
    cin>>_;
    while(_--) solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值