2024牛客寒假算法基础集训营1

A

找dfs这三个字符即可

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 55;

int n;
char s[N];

void solve()
{
	cin >> n >> s + 1;
	int pos1 = -1, pos2 = -1, pos3 = -1;
	int f1 = 1, f2 = 1;
	for(int i = 1; i <= n; i ++)
	{
		if(s[i] == 'D')
		{
			pos1 = i;
			break;
		}
	}
	if(pos1 == -1)f1 = 0;
	for(int i = n; i >= 1; i --)
	{
		if(s[i] == 'S')
		{
			pos3 = i;
			break;
		}
	}
	if(pos3 == -1)f1 = 0;
	for(int i = pos1 + 1; i < pos3; i ++)
	{
		if(s[i] == 'F')
		{
			pos2 = i;
			break;
		}
	}
	if(pos2 == -1)f1 = 0;
	
	pos1 = -1, pos2 = -1, pos3 = -1;
	for(int i = 1; i <= n; i ++)
	{
		if(s[i] == 'd')
		{
			pos1 = i;
			break;
		}
	}
	if(pos1 == -1)f2 = 0;
	for(int i = n; i >= 1; i --)
	{
		if(s[i] == 's')
		{
			pos3 = i;
			break;
		}
	}
	if(pos3 == -1)f2 = 0;
	for(int i = pos1 + 1; i < pos3; i ++)
	{
		if(s[i] == 'f')
		{
			pos2 = i;
			break;
		}
	}
	if(pos2 == -1)f2 = 0;
	
	cout << f1 << ' ' << f2 << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

B

枚举情况

考虑左右各堵住了几格和特判(1,-1)(1,1)(2,0)这三个点

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010;

void solve()
{
	int n;
	cin >> n;
	map mp;
	for(int i = 0; i < n; i ++)
	{
		int x, y;
		cin >> x >> y;
		mp[{x, y}] = 1;
	}
	
	int l = 0, r = 0;
	for(auto t : mp)
	{
		int x = t.first.first, y = t.first.second;
		//cout << "===" << x << ' ' << y << endl;
		if(y < 0)
		{
			l = max(l, 1);
			int tmp = 1;
			if(x == 1)tmp = 2;
			
			if(mp.count({tmp, y - 1}) || mp.count({tmp, y}) || mp.count({tmp, y + 1}))
			{
				l = 2;
			}
		}
		else if(y > 0)
		{
			r = max(r, 1);
			int tmp = 1;
			if(x == 1)tmp = 2;
			
			if(mp.count({tmp, y - 1}) || mp.count({tmp, y}) || mp.count({tmp, y + 1}))
			{
				r = 2;
			}
		}
	}
	
    int res = 4 - l - r;
	int tmp = 4;
    if(mp[{2, 0}])
    {
        tmp = 2;
        if(mp[{1, -1}])tmp --;
        if(mp[{1, 1}])tmp --;
        if(r == 2)res = min(res, 1);
        if(l == 2)res = min(res, 1);
    }
    res = min(res, tmp);
    if(mp[{1, -1}])
    {
        res = min(res, 2);
        if(mp[{1, 1}])res = min(res, 1);
    }
    if(mp[{1, 1}])
    {
        res = min(res, 2);
    }
    res = min(res, 3);
    cout << res << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

C

可以排序然后前缀和预处理出来在哪个人前面插队增加的时间,对每次询问可以二分来找

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010;

ll a[N], b[N];

void solve()
{
	int n, Q, t;
	cin >> n >> Q >> t;
	for(int i = 1; i <= n; i ++)cin >> a[i];
	sort(a + 1, a + 1 + n);
    for(int i = 1; i <= n; i ++)a[i] += a[i - 1];
	for(int i = 1; i <= n; i ++)//第i个时刻前插队 
	{
        b[i] = (ll)(n - i + 1) * t;
	}
	b[n + 1] = 0;
	
	while(Q --)
	{
		ll x;
		cin >> x;
		int l = 1, r = n + 1;
		while(l < r)
		{
			int mid = l + r >> 1;
			if(b[mid] <= x)r = mid;
			else l = mid + 1;
		}
		cout << a[l - 1] + t << endl;
	}
}

int main()
{
	IOS
	int _ = 1;
	//cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

E

范围只有10,可以3的n次方枚举所有情况

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 15;

int a[N], b[N];
int q1[N], q2[N];

void solve()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)cin >> a[i];
	
	for(int i = 0; i < m; i ++)
	{
		cin >> q1[i] >> q2[i];
	}
	
	int num = 1;
	for(int i = 0; i < m; i ++)num *= 3;
	int ans = 2e9;
	for(int i = 0; i < num; i ++)
	{
		for(int j = 1; j <= n; j ++)b[j] = a[j];
		int t = i;
		for(int j = 0; j < m; j ++)
		{
			int A = q1[j], B = q2[j];
			if(t % 3 == 0)
			{
				b[A] += 3;
			}
			else if(t % 3 == 1)
			{
				b[B] += 3;
			}
			else
			{
				b[A] ++;
				b[B] ++;
			}
			t /= 3;
		}  
		
		int x = b[1];
		sort(b + 1, b + 1 + n);
		int l = 1, r = n;
		while(l < r)
	    {
	        int mid = l + r + 1 >> 1;
	        if(b[mid] <= x)l = mid;
	        else r = mid - 1;
	    }
	    ans = min(ans, n + 1 - l);
	    
	}
	
    cout << ans << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

F

第二类斯特林数

将n个不同的元素分为k个集合 或者 将n个不同的小球放进k个相同的箱子

S(n, k) = S(n - 1, k) + k * S(n - 1, k)

n=k时为1,S(n, k) = 1

S(n, 0) = 0,特别地S(0, 0) = 1

容斥原理计算公式

S(n,m)=\frac{1}{m!}\sum_{k=0}^{m}(-1)^{k}C(m,k)(m-k)^{n}

搭配快速幂可以mlogn求出来

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010, mod = 1e9 + 7;
 
int n, m;
int fact[N], infact[N], inv[N];
 
int C(int a, int b)
{
    if(a < b)return 0;
	return (ll)fact[a] * infact[b] % mod * infact[a - b] % mod;
}

ll qmi(ll a, ll k)
{
	ll res = 1;
	while(k)
	{
		if(k & 1)res = res * a % mod;
		a = a * a % mod;
		k >>= 1;
	}
	return res;
}
 
int main()
{
	IOS
	
	fact[0] = fact[1]= infact[0] = infact[1] = inv[1] = 1;
	for(int i = 2; i < N; i ++)
	{
		fact[i] = ((ll)fact[i - 1] * i) % mod;
		inv[i] = (ll)(mod - mod / i) * inv[mod % i] % mod;
		infact[i] = (ll)infact[i - 1] * inv[i] % mod;
	}
	
	cin >> n >> m;
	int sign = 1;
	ll ans = 0;
	for(int k = 0; k <= m; k ++)
	{
		ll res = C(m, k) * qmi(m - k, n) % mod * sign;
		ans = (ans + res) % mod;
        sign *= -1;
	} 
	ans = (ans + mod) % mod;
	ll tmp = 1;
	for(int i = 2; i <= m; i ++)
	{
		tmp = tmp * i % mod;
	}
	ans = ans * qmi(tmp, mod - 2) % mod;
	cout << ans;
	
	return 0;
}

G

本金+b[i] >= a[i]时就说明可以买,可以先把所有b[i]加到本金里,然后从最大的a[i]往小枚举,不满足的话就-b[i]

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010;

PII a[N];

bool cmp(PII A, PII B)
{
	return A.first - A.second < B.first - B.second;
}

void solve()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		int x, y;
		cin >> x >> y;
		a[i] = {x, y};
	}
	
	sort(a + 1, a + 1 + n);
	ll t = m;
	for(int i = 1; i <= n; i ++)
	{
		t += a[i].second;
	}
	for(int i = n; i >= 1; i --)
	{
		if(t < a[i].first)
		{
			t -= a[i].second;
		}
	}
	cout << t << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

H

标题说是背包但其实解法跟背包也没什么关系

如果背包容量为10101  (二进制)

物品重量为10101 10011

如果选了第一个物品第二个就选不了,如果选了第二个第一个就选不了,无法同时选

可以考虑将背包容量的某一位1强制转为0,然后这一位上为1的物品都不选,只要在这一位之前,物品的重量的位为1时背包这一位也为1就说明可选。这一位之后怎么都无所谓的,不论怎么选都会小于原背包容量的

这样在每次过程中每个物品都变成了可选和不可选两种情况。

然后背包原重量也这么来一遍,取一个最大值即可

这样枚举是包含所有情况的

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010;

int n, m;
int v[N], w[N];

void solve()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> v[i] >> w[i];
	}
	
	ll ans = 0;
	for(int i = 1; i <= n; i ++)
	{
		if((w[i] | m) == m)
		{
			ans += v[i];
		}
	}
	
	while(m)
	{
		if(m & 1)
		{
			m >>= 1;
			ll res = 0;
			for(int i = 1; i <= n; i ++)
			{
                if(w[i] & 1)
                {
                    w[i] >>= 1;
                    continue;
                }
                w[i] >>= 1;
				if((w[i] | m) == m)
				{
					res += v[i];
				}
			}
			ans = max(ans, res);
		}
		else 
        {
            m >>= 1;
            for(int i = 1; i <= n; i ++)w[i] >>= 1;
        }
	}
	cout << ans << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
}

I

正统解法其实是看点的分布概率,第一个人的点分布概率是平均的,第二个人的点分布概率是偏中心的。

我想的是算两个人半径的期望,第二个人的期望一定比第一个人的期望大

最终半径的平均值离谁更近就说明是谁,那肯定有一个临界值,我的想法是找这个临界值

或者也可以真的按这两个人的办法分别模拟1e5或者更多次,看期望是多少

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

int main()
{
	IOS
    
    int n;
	cin >> n;
	double r = 0;
	for(int i = 1; i <= n; i ++)
	{
		double tmp;
		cin >> tmp >> tmp >> tmp;
		r += tmp;
	}
	r /= (double)n;
	
	if(r <= 20.0)cout << "bit-noob";
    else cout << "buaa-noob";
	
	
	return 0;
}

K

一个题的选项是对应题的答案,可以构建出一个基环树(其实是若干个基环树,或者说森林)。

一个环可以有好几条链,所以从链的起点出发其实不好算,但也可以发现后者答案固定后前者的答案是可以逆推出来的,所以可以从一个环上的任意一个点当成起点出发,枚举A-F选项,如果选项合理就计入答案

最终答案数其实就是每个环的方案数相乘

这里学到了一种很好写的方法,用vector存这次遍历到的所有点,然后看最后一个点的下一个点是否在vector中,如果在就说明有环,把这个点之前的点清除剩下的就是一个环。如果最后一个点的下一个点没在vector中就说明没环(环已经被标记了),非常方便的写法

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010, mod = 998244353;

int n;
int a[N];
string s[N];
bool st[N];

int main()
{
	IOS
	cin >> n;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i] >> s[i];
	}
	
	ll ans = 1;
	for(int i = 1; i <= n; i ++)
	{
		if(st[i])continue;
		int j = i;
		vector v;
		for(; !st[j]; j = a[j])
		{
			st[j] = true;
			v.push_back(j);
		}
		
		vector::iterator it = find(v.begin(), v.end(), j);
		if(it == v.end())continue;
		
		v.erase(v.begin(), it);//这样只剩下环里的点了
		ll res = 0;
		for(char ch = 'A'; ch <= 'E'; ch ++)
		{
			int t = ch - 'A';
			for(auto x : v)
			{
				t = s[x][t] - 'A';
			}
			if(ch - 'A' == t)res ++;
		}
		ans = ans * res % mod;
	}
	cout << ans;
	
	return 0;
}

L

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 100010;

ll a[N], b[N];

void solve()
{
	ll c, d, h, w;
    cin >> c >> d >> h >> w;
	cout << 3 * w * c << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

M

#include
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
 
using namespace std;
 
typedef pair PII;
typedef long long ll;

const int N = 55;

void solve()
{
	int n;
	cin >> n;
	if(n % 6)
	{
		cout << n / 6 * 2 << endl;
	}
	else cout << n / 6 << endl;
}

int main()
{
	IOS
	int _;
	cin >> _;
	while(_ --)
	{
		solve();
	}
	
	return 0;
} 

你可能感兴趣的:(牛客,算法,c++)