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();
}