牛客 周赛85 20250320

牛客 周赛85 20250320

https://ac.nowcoder.com/acm/contest/103948

A:

题目大意:有 \(n\) 个石头,每次取走一个判断谁获胜

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
  
using namespace std;
 
int n;
 
int main()
{
    cin>>n;
    if (n%2==1) cout<<"kou";
    else cout<<"yukari";
     
    return 0;
}

签到

B:

题目大意:\(n\) 个元素的数组 \(a\) ,两个人轮流操作,先手的人希望答案最小,后手的人希望答案最大

每次挑选一个元素删除,先手的人会使答案加上 \(a_i\) ,后手的人会使答案减去 \(a_i\) ,在双方都采取最佳方式时,求最终的答案数

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
  
using namespace std;
 
int n;
int a[100010];
 
int main()
{
    cin>>n;
    for (int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
    LL sum=0;
    for (int i=1;i<=n;i++){
        if (i%2) sum+=1ll*a[i];
        else sum-=1ll*a[i];
    }
    cout<<sum;
    return 0;
}

考虑到每个 \(a_i\) 都是正整数,所以先手的人选择当前数组中最小的正整数是他的最优策略

C:

题目大意:给定的由 \(0,1\) 组成的字符串,两个人最多可以进行如下操作最多一次

  • 甲可以选择一个全 \(1\) 的连续子串使其全部变为 \(0\)
  • 乙可以选择一个全 \(0\) 的连续字串使其全部变为 \(1\)

判断这两人是否能把给定的字符串所以字符都变为相同

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
  
using namespace std;
 
void solve(void){
    string s;
    cin>>s;
    s=s[0]+s;
    int a=0,b=0;
    if (s[1]=='1') a++;
    else b++;
    for (int i=1;i<s.size();i++){
        if(s[i]!=s[i-1]){
            if (s[i]=='1') a++;
            else b++;
        }
    }
    if (a<=2&&b<=2 || abs(a-b)==1&&max(a,b)==3)
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
}
 
int main()
{
    Trd;
     
    return 0;
}

遍历字符串找字符相同的连续子串的个数,手玩出结论是

01010 0101 010 01 0 

类似上面五种的字符串形式可以使得最终字符串全相同

D:

题目大意:存在一个 \(n\) 个字符的字符串 \(s\) ,甲先手可以删除这个字符串的一个非空前缀,乙后手可以删去这个字符串的后缀(可以为空),计算乙可以将这个字符串变为双生串的概率

*双生串:一个字符串中,每一种元素出现的次数都为偶数次

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
  
using namespace std;
 
int n;
 
int main()
{
    cin>>n;
    string s;
    cin>>s;
    s=s[0]+s;
    int p=0;
    for (int i=1;i<=n;i++){
        int a=0,b=0;
        for(int j=i+1;j<=n;j++){
            if (s[j]=='1') a++;
            else b++;
            if (a%2==0&&b%2==0){
                p++;
                break;
            }
        }
    }
    cout<<(double)p/n;
    return 0;
}

枚举删去的前缀长度,在该位置上向后暴力枚举,判断是否存在双生子串,因为字符串中都是 \(0,1\) ,所以第二层暴力最多枚举 \(4\) 个位置

所以总时间复杂度为 \(O(n)\)

E:

题目大意:

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18
#define Iinf 2e9
#define LL long long
#define ULL unsigned long long 
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
 
using namespace std;
 
struct line{
    int l,r,id;
    bool operator<(const line &x) const{
        if (l==x.l)
             return r<x.r;
        return l<x.l;
    }
};
 
line L[100010];
 
void solve(){
    int n;
    cin>>n;
    for (int i=1;i<=n;i++){
        cin>>L[i].l>>L[i].r;
        L[i].id=i;
    }
    vector<pair<int,int>> d;
    for (int i=1;i<=n;i++){
        d.push_back({L[i].l,1});
        d.push_back({L[i].r+1,-1});
    }
     
    sort(d.begin(),d.end());
    int sum=0;
    for (auto [x,y]:d){
        sum+=y;
        if (sum>2){
            cout<<-1<<endl;
            return;
        }
    }
    sort(L+1,L+n+1);
    int r=-1,id=0;
    vector<int> ans(n+1);
    for (int i=1;i<=n;i++){
        if (L[i].l<=r)
            ans[L[i].id]=ans[id]^1;
        if (L[i].r>r){
            r=L[i].r;
            id=L[i].id;
        }
    }
    vector<int> res;
    for (int i=1;i<=n;i++){
        if (!ans[i])
            res.push_back(i);
    }
    cout<<res.size()<<endl;
    for (auto it:res)
        cout<<it<<' ';
}
 
int main()
{
    solve();
    
    return 0;
}

思维题,当一个位置上能被三个及以上的线段覆盖时,那么一定不存在合法的染色满足题意

vector<pair<int,int>> d;
for (int i=1;i<=n;i++){
    d.push_back({L[i].l,1});
    d.push_back({L[i].r+1,-1});
}

采用差分维护这个性质,每个线段左端点为 \(1\) 右端点的下一个位置为 \(-1\)

对维护的差分数组排序按照左端点排序后,从左到右遍历线段

sort(d.begin(),d.end());
int sum=0;
for (auto [x,y]:d){
    sum+=y;
    if (sum>2){
       cout<<-1<<endl;
        return;
    }
}

如果当前位置上的 sum 大于 \(2\) ,说明被三个及以上的线段覆盖,输出 \(-1\) 返回

然后考虑满足条件,构造一个合适的染色方案

每个线段的染色方案仅取决于他的前一个线段的右区间的位置

sort(L+1,L+n+1);
int r=-1,id=0;//r记录上一个线段的右区间
vector<int> ans(n+1);
for (int i=1;i<=n;i++){
    if (L[i].l<=r)//如果当前线段的左区间在r的左侧,说明这两个线段相交
        ans[L[i].id]=ans[id]^1;//将这个线段的颜色染为r所属的线段的反色 1^0=1,1^1=0
    if (L[i].r>r){//如果当前线段的右区间在r的右边,那么需要更新r与r对应线段的颜色
        r=L[i].r;
        id=L[i].id;
    }
}

F:

题目大意:给定 \(n\) 个节点的树,每个节点一开始都是红色,甲准备将 \(k\) 个节点染成紫色,使得红色连通块的大小尽可能小,求出红色连通块的最少数量

#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
  
using namespace std;
 
int n,k;
vector<int> e[100010];
int sub[100010];
 
bool check(int mid,int &res,int x,int p){
    sub[x]=1;
    for (auto v:e[x]){
        if (v==p) continue;
        if (!check(mid,res,v,x)) return 0;
        sub[x]+=sub[v];
    }
    if (sub[x]>mid){
        res++;
        sub[x]=0;
        if (res>k) return 0;
    }
    return 1;
}
 
bool judge(int mid){
    int res=0;
    return check(mid,res,1,-1);
}
 
int main()
{
    cintie;
    cin>>n>>k;
    for (int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    int l=-1,r=n+1;
    while(l+1!=r){
        int mid=l+r>>1;
        if (judge(mid))
            r=mid;
        else
            l=mid;
    }
    cout<<r;
    return 0;
}

二分连通块的最小大小,DFS 计算每个节点的子树大小

bool check(int mid,int &res,int x,int p){
    sub[x]=1;//子树大小初始化为1
    for (auto v:e[x]){
        if (v==p) continue;
        if (!check(mid,res,v,x)) return 0;//连锁回溯
        sub[x]+=sub[v];//回溯累加子树大小
    }
    if (sub[x]>mid){//如果当前节点的子树大小超过了二分的值
        res++;//染色数+1
        sub[x]=0;//将这个节点的子树大小设为0回溯,即剪掉他对父节点的贡献
        if (res>k) return 0;//如果染色数大于了k,说明当前的二分值太小
    }
    return 1;//如果当前的二分值合法,考虑缩小右边界
}
posted @ 2025-03-28 17:29  才瓯  阅读(10)  评论(0)    收藏  举报