牛客 周赛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;//如果当前的二分值合法,考虑缩小右边界
}