牛客第四场

Just a joker

请添加图片描述
题意
一个图,有n个点,m条边
Alice 和Bob 可以执行两种操作,一种是删去一条边,一种是删去一个不含环的连通分支,(注意,删去边的时候并没有将边上的点删去)
Alice先操作,最后不能进行操作的人输,最后谁会赢得这个游戏
我们可以发现,不论是第一种操作还是第二种操作,删去的边和点的和都是一个奇数,所以我们只需要注意边和点的和是奇数还是偶数就可以做出这题了,如果是奇数的话就是Alice胜,否则就是Bob胜;

#include<iostream>
using namespace std;
void solve(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int u,v;
        cin>>u>>v;
    }
    if((n+m)%2==0)cout<<"Bob"<<endl;
    else cout<<"Alice"<<endl;
}
int main(){
    solve();
}

LCS

请添加图片描述
题意
让我们构造三个字符串使得s1,s2中相同的字符个数是a,s2,s3中相同的字符个数是b,s3,s1中相同的字符个数是c,三个字符串的长度都为n
如果能构造就输出其中的一种,不行就输出NO
我们可以先找出abc中最小的那一个,如果这个数字是a,因为这三个字符串一定都满足相同的 字符个数大于这个值,我们可以先将三个字符串中的前a个字符都赋值成一个,然后按照文氏图将相交的部分填上字符,最后补全,如果补全之后有字符串的长度大于n的话,那么就代表我们不能构造出这样一个字符串,就输出NO;

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
int a,b,c,n;
void solve(){
    cin>>a>>b>>c>>n;
    string s1,s2,s3;
    int minx=min(min(a,b),c);
    for(int i=0;i<minx;i++){
       s1+='a';
       s2+='a';
       s3+='a';
    }
    for(int i=0;i<a-minx;i++){
        s1+='b';
        s2+='b';
    }
    for(int i=0;i<b-minx;i++){
        s2+='c';
        s3+='c';
    }
    for(int i=0;i<c-minx;i++){
        s3+='d';
        s1+='d';
    }
    for(int i=s1.size();i<n;i++)s1+='e';
    for(int i=s2.size();i<n;i++)s2+='f';
    for(int i=s3.size();i<n;i++)s3+='g';
    if(s1.size()>n||s2.size()>n||s3.size()>n)cout<<"NO"<<endl;
    else cout<<s1<<endl<<s2<<endl<<s3<<endl;
}
int main(){
    solve();
}

Inverse Pair

请添加图片描述
题意
给出一个数列,将其中的值加一或者是不加,对于每一个数只能做一次操作,对于每一个数进行这样的操作后,最小的逆序对数是多少;
题解
那么我们可以思考,怎样可以减少逆序对的数量呢;在后面将一个小数加一就可以减少一个,可以发现,如果将产生逆序对的每一个较小的数都加一只会减少一个逆序对数量,这样肯定是不行的,而如果将前面较大的数加一只会使我们的结果越来越大,所以肯定不能将未产生逆序对的数字加一
那么在出现逆序对的部分,要怎么做才能将逆序对减少到最小的,是不是只有将产生逆序对的部分按一对一对的分出来,如果每一个都可以减小一个逆序对,那么最后减少的是最小的呢,(并且还要要求逆序对中的数各不相同)
三个一组四个一组一定比两个一组大我们可以知道,而这样能减少的逆序对中两个数的差值一定是一,所以我们可以通过一个结构体或者一个pair来分别存住他们的下标和值,将他们按值排序,将产生逆序对的两两一组,用最开始的逆序对减去这样的对数,就是答案

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N=2e5;
struct node{
    int x,y;
    bool operator <(const node & u){return y<u.y;}
}f[N];
bool cmp(node u,node v){
    return u.x<v.x;
}
long long sum=0;
vector<int>a(N),b(N);
void merge(int l, int r) {
	if (l >= r)return;
	int mid = l + r >> 1;
	merge(l, mid);
	merge(mid + 1, r);
	int k = 0, i = l, j = mid + 1;
	while (i <= mid && j <= r) {
		if (a[i] <= a[j])b[k++] = a[i++];
		else {
			b[k++] = a[j++];
			sum +=( mid - i + 1);
		}
	}
	while (i <= mid) {
		b[k++] = a[i++];
	}
	while (j <= r) {
		b[k ++] = a[j++];
	}
	for (int i = l, j = 0; i <= r; i++, j++) {
		a[i] = b[j];
	}
}
void solve(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        f[i].x=a[i];
        f[i].y=i;
    }
    merge(0,n-1);
    sort(f,f+n,cmp);
    for(int i=n-1;i>=1;i--){
        if(f[i-1].y>f[i].y)sum--,i--;
    }
    cout<<sum<<endl;
}
int main(){
	ios::sync_with_stdio(0); 
	cin.tie(0); 
	cout.tie(0); 
	cin.exceptions(ios::badbit | ios::failbit);
    solve();
}

Average

请添加图片描述
题意
给出一个N*M的矩阵以及一个数k,求出这个矩阵的一个子矩阵(行和列都大于等于k),使得这个子矩阵的平均值最大
我们可以从平均值出发,二分答案,如果我们能找到平均值大于(l+r)/2并且区间长度>=k 的区间,那么我们就可以找到更大的平均值
我们可以将每个元素 -average,使区间平均值 -average,将问题转化成求区间平均值增大还是减小;
然后求出这个 -average的数组的前缀和,枚举终点的位置,使得[终点]-[起点] >=0,并且长度大于等于k
所以我们只需要找出这个新数组的最小前缀和就好了

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int M=3e7+10;
const int INF=0x3f3f3f3f;
double a[N],b[N],tmp[N]; 
bool check(double num[], int n, int k, double avg)
{
    for (int i = 1; i <= n; i ++ ) tmp[i] = num[i] - avg;// 让每个元素都 - avg,相当于区间平均值 - avg,问题转化为求区间平均值是否能 >= 0
 
    for (int i = 1; i <= n; i ++ ) tmp[i] += tmp[i - 1];// 求前缀和
 
    //枚举终点,[k, n];由于区间长度只要 >= k 即可,起点具体是什么并不重要,只要满足tmp[终点] - tmp[起点] >= 0,且区间长度满足即可,所以关于起点只需要随着终点的进行,一直更新为最小值即可
    double mi = 1e9 + 10;
    for (int i = k; i <= n; i ++ )
    {
        mi = min(mi, tmp[i - k]);// mi == 最小的起点前缀和,使得tmp[终点] - tmp[起点] >= 0中的tmp[起点]最小,
        if (tmp[i] - mi >= 0) return true;// tmp[i] - tmp[i - k]
    }
 
    return false;
}
 
double query (double num[], int n, int k)//在长度为n的num数组中找区间长度 >= k的区间中平均值最大的区间
{
    double l = -1e5, r = 1e5;
 
    while (r - l >= 1e-8)
    {
        double mid = (l + r) / 2;
        if (check(num, n, k, mid)) l = mid;//如果可以在num中找到平均值 >= mid 且区间长度 >= k的区间,那么可以找更大的平均值
        else r = mid;
    }
 
    return r;
}
void solve(){
    int n,m,x,y;
	cin>>n>>m>>x>>y;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++)cin>>b[i];
	cout<<fixed<<setprecision(6)<<query(a,n,x)+query(b,m,y)<<endl;
}
int main(){
	ios::sync_with_stdio(0); 
	cin.tie(0); 
	cout.tie(0); 
	cin.exceptions(ios::badbit | ios::failbit);
	solve();
}

Sample Game

请添加图片描述
题意
由题意可知,我们在游戏结束之前得到的一定是一个不严格单增的序列,而最后一个数一定比倒数第二个数小,这样我们就可以枚举每个数出现的次数有一个唯一的序列在这里插入图片描述
然后就是咱也不大懂的推导在这里插入图片描述
因为有除法,所以优先采用逆元的方法,重f(1)逐个往回算完;

#include<iostream>
#include<vector>
using namespace std;
const int N = 1e5 +10;
const int P = 998244353;
int p[N];
int n;
int pow(int a, int b) {
	int c = 1;
	for (; b; b >>= 1, a = a * 1ll * a % P)if (b & 1)c = c * 1ll * a % P;
	return c;
}
void solve() {
	cin >> n;
	int sum = 0;
	for(int i=1;i<=n;i++)cin>>p[i], sum = (sum + p[i]) % P;
	sum = pow(sum, P - 2);
	for(int i=1;i<=n;i++)p[i] = p[i] * 1ll * sum % P;
	int ans = 1;
	for(int i=1;i<=n;i++)ans = ans * 1ll * pow(P + 1 - p[i], P - 2) % P;
	int w = 0;
	for(int i=0;i<=n;i++) {
		w = (w + p[i] * 1ll * pow(P + 1 - p[i], P - 2)) % P;
	}
	ans = (ans + 2ll * ans * w) % P;
	cout << ans << endl;
}
int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin.exceptions(ios::badbit | ios::failbit);
	solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值