Educational Codeforces Round 113 (Rated for Div. 2) (A-C)

A. Balanced Substring

题目

一个只有’a’,'b’的串,求一个子串使得 a的个数和b相等

思路

AC代码

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 100 ,mod=1e9 + 7;
char str[N];
int n;
int sa[N],sb[N];
void solve()
{    
	cin>>n>>str+1;
	for(int i=1;i<=n;i++)
	{
		if(str[i]=='a') sa[i] = sa[i-1]+1,sb[i]=sb[i-1]+0;
		else sa[i]=sa[i-1],sb[i]=sb[i-1]+1;
	}
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
		{
			int a = sa[j]-sa[i-1],b=sb[j]-sb[i-1];
			if(a==b)
			{
				cout<<i<<' '<<j<<endl;
				return;
			}
		}
	cout<<"-1 -1\n";
}
signed main()
{
	ios::sync_with_stdio();cin.tie();cout.tie();

	int T;cin>>T;
	while(T--)
		solve();

	return 0;
}

B. Chess Tournament

题目

n个人对局,每个人都和其他人斗一场,有胜,平,败三种可能。有两种人

  1. 不败就行
  2. 至少赢一次

求一种对局方案,满足所有人需求。

思路

  • 对第一种人,不能败,但如果他赢,对方案的构造有害无益(其他人会输,他赢,但他又不需要赢),所以对第一类的贪心策略就是全平
  • 对第二种人,要赢一次,他只能找同类人赢(第一类不败)而且赢一次就行,其他的输了就行。

很简单的贪心思路,主要代码实现有点麻烦。详见代码注释。

AC代码

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
//#define int long long
using namespace std;
typedef pair<int, int> PII;
const int N = 10 + 50, mod = 1e9 + 7;
int n;
char s[N];
char m[N][N];
void solve()
{
	cin >> n >> s + 1;
    // 初始化
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			m[i][j] = '.';
	for (int i = 1; i <= n; i++)
		m[i][i] = 'X';
    // 处理第一类 全平,对称更新矩阵
	for (int i = 1; i <= n; i++)
		if (s[i] == '1')
		{
			for (int j = 1; j <= n; j++)
			{
				if (m[i][j] == '.')
					m[j][i] = m[i][j] = '=';
			}
		}
    // 处理第二类,给赢得打个标记
	bool win[N];
	memset(win, 0, sizeof win);
	for (int i = 1; i <= n; i++)
	{
		if (s[i] == '1')
			continue;
		if (!win[i])
		{
			int j = 1;
			while (j <= n && m[i][j] != '.')// 找到第一个未处理的场次
				j++;
			if (m[i][j] == '.')
			{
				m[i][j] = '+', m[j][i] = '-';
				win[i] = 1;
			}
			else break;// 如果没有未处理的 说明没法满足要求break
		}
	}
    // 判不可行
	for (int i = 1; i <= n; i++)
	{
		if (s[i] == '2' && !win[i])
		{
			cout << "NO\n";
			return;
		}
	}
    // 让其他未处理的场都平局就行(处理成胜败也可)
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(m[i][j]=='.')m[i][j]='=';
	puts("YES");
	for (int i = 1; i <= n; i++, puts(""))
		for (int j = 1; j <= n; j++)
			cout << m[i][j];
}
signed main()
{
	ios::sync_with_stdio();
	cin.tie();
	cout.tie();

	int T;
	cin >> T;
	while (T--)
		solve();

	return 0;
}

C. Jury Meeting

题目

n个人发言,每个人都可以分一个次数,轮流发言,次数用尽就跳过,求有多少的分配排列可以使没有一个人连续发言。没有输出0.

思路

  • 先考虑怎样的次数序列总不合法,对于序列最大值 m a x max max,如果它很大显然会有人连续发言,现在找到这个“大的界限”。
  • 因为每一轮都会使发言次数减一,并且如果连续发言,此时一定只有一个次数非零的人,也就是说只要有一个人能充当“间隔” , 就不会使得人连续发言。
  • 因此考虑最大值 a a a和第二大值 b b b的关系。
    1. a = b a=b a=b时,显然在最后会是它们的交替,所有排列都合法。
    2. a − b > 1 a-b>1 ab>1时,总会出现连续,此时答案就为0
    3. a − b = 1 a-b=1 ab=1时,必须至少有一个b排在a的后面,详见下例。
/* 
2
1 2

1 2 ->  122 ->no
2 1 ->  212 ->yes
 */
  • 因此,现在只需要解决如何求最大值右边至少有一个次大值的排列数即可。
    设次大值有 t o t tot tot个,考虑这 t o t + 1 tot+1 tot+1个数,右边一个次大值都没的可能性是 1 1 + t o t \frac{1}{1+tot} 1+tot1 ,所求概率为 t o t 1 + t o t \frac{tot}{1+tot} 1+tottot.
    所以全排列乘上这个概率就行。

AC代码

时间复杂度: O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define debug(x) cout<<"> "<< x<<endl;
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 2e5 ,mod=998244353;
int n;
int a[N];
ll p[N];

ll ksm(ll _a,ll b)
{
    ll res = 1 ;
    while(b)
    {
        if(b&1) res=(res*_a)%mod;
        _a=(_a*_a)%mod;
        b>>=1;
        // debug(res)
    }
    return res%mod;
}

void solve()
{    
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    int tot = count(a+1,a+1+n,a[n]);
    if(tot>=2)
        cout<<p[n]<<endl;
    else 
    {
        tot = count(a+1,a+1+n,a[n]-1);
        if(tot==0)
            puts("0");
        else
        {
            // tot/(tot+1) * x;
            cout<<1ll*p[n]*tot%mod*ksm(tot+1,mod-2) %mod<<endl;
        }
    }
}
signed main()
{
    ios::sync_with_stdio();cin.tie();cout.tie();
    
    p[0]=1;
    for(int i=1;i<N;i++) p[i] = p[i-1]*i%mod;
    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}

后记

取模古古怪怪,要专门看看安全取模的技巧。还有排列组合,也感觉懵逼的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值