2024牛客暑期多校训练营1

A. A Bit Common

如果一个子序列的 A N D AND AND值为 1 1 1,则这个子序列中的数的二进制最低位全为 1 1 1,除去二进制最低位的其他 m − 1 m-1 m1位至少有一个 0 0 0,那我们就把数分成二进制最低位为 1 1 1和最低位为 0 0 0来考虑,计算所有二进制最低位为 1 1 1的数 A N D AND AND值为 1 1 1的情况。

记二进制最低位为 1 1 1的数有 k k k个,由于不同顺序的序列对答案的贡献是不一样的,如 { 1 , 2 , 3 } \{1,2,3\} {1,2,3} { 1 , 3 , 2 } \{1,3,2\} {1,3,2}算作两个不同的序列,所以我们要计算这 k k k个数出现在什么位置,情况有 ( n k ) \left(\begin{array}{c}n \\ k\end{array}\right) (nk)种,如果剩余 m − 1 m-1 m1位随意取值的话,情况有 2 k ( m − 1 ) 2^{k(m - 1)} 2k(m1)种,但是前文提到这 m − 1 m-1 m1位上每位至少存在一个 0 0 0,所以对于每一位都要去除掉全为 1 1 1的情况,所以情况只有 ( 2 k − 1 ) m − 1 (2^k-1)^{m-1} (2k1)m1种情况。

二进制最低位为 0 0 0的数只剩 n − k n-k nk个了,除去二进制的最低位还剩 m − 1 m-1 m1位,因为 A N D = 1 AND=1 AND=1的情况已经在二进制最低位为 1 1 1的数中出现了,所以这些位可以随意取值,共有 2 ( n − k ) ( m − 1 ) 2^{(n-k)(m-1)} 2(nk)(m1)种情况。

所以最后的答案是 ∑ k = 1 n ( n k ) ( 2 k − 1 ) m − 1 2 ( n − k ) ( m − 1 ) \sum_{k=1}^{n}\left(\begin{array}{c}n \\ k\end{array}\right)(2^k-1)^{m-1}2^{(n-k)(m-1)} k=1n(nk)(2k1)m12(nk)(m1),时间复杂度 O ( n 2 ) O(n^2) O(n2)

还有个小细节,因为模数 q q q可以取到 1 1 1且数据范围较小,所以组合数使用递推式。

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
//const int mod = 1e9 + 7;
int C[5005][5005];
int n, m, mod;
inline ll binpow(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}
void solve()
{
	cin >> n >> m >> mod;
	for(int i = 0;i <= n;i++)
	{
		C[i][0] = C[i][i] = 1;
		for(int j = 1;j <= i - 1;j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
	}
	int ans = 0;
	for(int k = 1;k <= n;k++)
	{
		int t1 = C[n][k] * binpow(binpow(2, k) - 1, m - 1) % mod;
		int t2 = binpow(2, (n - k) * (m - 1));
		ans = (ans + t1 * t2 % mod) % mod;
	}
	cout << ans << '\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	solve();
	return 0;
}

B. A Bit More Common

A A A题不一样的是,这题需要序列中至少有两个子序列满足 A N D = 1 AND=1 AND=1,正难则反,我们来计算序列中只有一个子序列满足 A N D = 1 AND=1 AND=1的情况,用上一题算出来的结果减去这种情况的数量即为所求。

因为只有一个子序列满足 A N D = 1 AND=1 AND=1,所以当该子序列中去掉一个元素后剩下的数 A N D ≠ 1 AND\neq1 AND=1

同样的,满足 A N D = 1 AND=1 AND=1的子序列除去二进制的最低位,剩余的 m − 1 m-1 m1列至少有一个 0 0 0,我们只关心那些只有一个 0 0 0的列,把这些列称为关键列,一个关键列可以对某一行做出贡献。

所以只要满足每一行都被关键列覆盖就满足条件,枚举关键列的数量 t t t和行数 k k k,行数即为选择的二进制最低位为 1 1 1的数的数量。令 f [ k ] [ t ] f[k][t] f[k][t] t t t个关键列覆盖 k k k行的方案数。因为可能出现一个行被多个关键列覆盖的情况,所以移除某个关键列后被覆盖的行数可能为 k k k或者 k − 1 k-1 k1,那我们就可以写出转移方程: f [ k ] [ t ] = k ∗ ( f [ k ] [ t − 1 ] + f [ k − 1 ] [ t − 1 ] ) f[k][t]=k*(f[k][t-1]+f[k-1][t-1]) f[k][t]=k(f[k][t1]+f[k1][t1])除去关键列,还剩下 m − 1 − t m-1-t m1t列,这些列不能被选为关键列且要减去全为 1 1 1的情况,所以每一列都有 2 k − k − 1 2^k-k-1 2kk1种方案可选。由于 k = 1 k=1 k=1时所有情况都不行,

所以最后的总数为 ∑ k = 2 n ( n k ) 2 ( n − k ) ( m − 1 ) ( m − 1 t ) ( 2 k − k − 1 ) m − 1 − t \sum_{k=2}^{n}\left(\begin{array}{c}n \\ k\end{array}\right)2^{(n-k)(m-1)}\left(\begin{array}{c}m-1 \\ t\end{array}\right)(2^k-k-1)^{m-1-t} k=2n(nk)2(nk)(m1)(m1t)(2kk1)m1t,用上一题的答案减去这个式子的值即为所求。

值得注意的是,这题需要预处理一些数据,不然 O ( n 2 l o g n ) O(n^2logn) O(n2logn)的时间复杂度会超时。

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
//const int mod = 1e9 + 7;
int n, m, mod;
int f[5005][5005], C[5005][5005], pre[25000005], pre2[5005];
inline ll binpow(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}
void solve()
{
	int ans1 = 0, ans2 = 0;
	cin >> n >> m >> mod;
	pre[0] = 1;
	for(int i = 1;i <= n * m;i++) pre[i] = pre[i - 1] * 2 % mod;
	for(int i = 0;i <= max(n, m);i++)
	{
		C[i][0] = C[i][i] = 1;
		for(int j = 1;j <= i - 1;j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
	}
	f[0][0] = 1;
	for(int k = 1;k <= n;k++)
	{
		for(int t = k;t <= m - 1;t++) f[k][t] = k * (f[k][t - 1] + f[k - 1][t - 1]) % mod;
	}
	for(int i = 2;i <= n;i++)
	{
		ans1 = (ans1 + C[n][i] * pre[(n - i) * (m - 1)] % mod * binpow(pre[i] - 1, m - 1)) % mod;
	}
	for(int k = 2;k <= n;k++) // 最低为为1的数
	{
		int base = pre[k] - k - 1;
		int now = 1;
		for(int t = m - 1;t >= k;t--) // 关键列数
		{
			int t1 =  C[n][k] * pre[(n - k) * (m - 1)] % mod;
			int t2 = C[m - 1][t] % mod * f[k][t] % mod * now % mod;
			ans2 = (ans2 + t1 * t2) % mod;
			now = now * base % mod;
		}
	}
	cout << (ans1 - ans2 + mod) % mod << '\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	solve();
	return 0;
}

C. Sum of Suffix Sums

维护 a [ i ] ∗ i a[i]*i a[i]i的前缀和,每次询问直接输出当前坐标的答案,时间复杂度 O ( q ) O(q) O(q)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 1e9 + 7;
int a[N];
void solve()
{
	int q, now = 0;
	cin >> q;
	while(q--)
	{
		int t, v;
		cin >> t >> v;
		now -= t;
		now++;
		a[now] = now * v % mod;
		a[now] = (a[now] + a[now - 1]) % mod;
		cout << a[now] << '\n';
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	solve();
	return 0;
}

H. World Finals

l z r 010506 lzr010506 lzr010506参加第一场,其他队伍都参加第二场或者 l z r 010506 lzr010506 lzr010506参加第二场,其他队伍都参加第一场,两个名次取高,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e18 + 100;
const int mod = 1e9 + 7;
struct sj
{
	string a;
	int b, c;
} x[N], y[N];
bool cmp(sj x, sj y)
{
	if(x.b == y.b) return x.c < y.c;
	else return x.b > y.b;
}
void solve()
{
	map<string, int> mp1, mp2;
	int n, m;
	cin >> n;
	for(int i = 1;i <= n;i++)
	{
		cin >> x[i].a >> x[i].b >> x[i].c;
		mp1[x[i].a] = 1;
	}
	cin >> m;
	for(int i = 1;i <= m;i++)
	{
		cin >> y[i].a >> y[i].b >> y[i].c;
		mp2[y[i].a] = 1;
	}
	sort(x+1,x+n+1,cmp);
	sort(y+1,y+m+1,cmp);
	int t1 = 1, t2 = 1;
	for(int i = 1;i <= n;i++)
	{
		if(x[i].a == "lzr010506") break;
		t1++;
		if(mp2.count(x[i].a)) t1--;
	}
	for(int i = 1;i <= m;i++)
	{
		if(y[i].a == "lzr010506") break;
		t2++;
		if(mp1.count(y[i].a)) t2--;
	}
	cout << min(t1, t2) << '\n';
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	solve();
	return 0;
}

I. Mirror Maze

由于光的可逆性,所以在图中光路不会相交,所以图中的光路可以看作若干个链和若干个环。如果是链,那么经过反射一定会从某个镜子射出。根据光的可逆性,我们从外面射入光线,那么这束光一定会射出,通过记录并处经过的点和方向我们可以得到所有链的情况。环则同理,遍历每个镜子,如果没有射出则是环,预处理后每次询问直接输出答案,时间复杂度 O ( n m ) O(nm) O(nm)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef __uint128_t ulll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const double eps = 1e-9;
const int N = 1e3 + 10;
const int INF = 1e18 + 100;
const int mod = 1e9 + 7;
char boa[N][N];
int vis[N][N], vis2[N][N][4], dp[N][N][4];//dp[x][y][d] 代表(x, y)的坐标点向d传播
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
int os, n, m;
void dfs(int x, int y, int d, int cur)
{
	x += dir[d][0];
	y += dir[d][1];
	if(x < 1 || x > n || y < 1 || y > m) return;
	dp[x][y][d ^ 2] = cur;
	int add = 0;
	if(boa[x][y] == '-')
		if(d == 1 || d == 3)
		d = 4 - d, add = 1;
	if(boa[x][y] == '|')
		if(d == 0 || d == 2)
			d = 2 - d, add = 1;
	if(boa[x][y] == '/')
		d = 3 - d, add = 1;
	if(boa[x][y] == '\\')
    {
		if(d == 2 || d == 3) d = 5 - d;
		else d = 1 - d;
		add = 1;
	}
	if(vis[x][y] == os) add = 0;
	if(add) vis[x][y] = os;
	dfs(x, y, d, cur + add);
}
struct node
{
	int x, y, d;
};
vector<node>po;
vector<pii>pi;
void dfs2(int x, int y, int d)
{
	x += dir[d][0];
	y += dir[d][1];
	if(x < 1 || x > n || y < 1 || y > m) return;
	int add = 0;
	if(boa[x][y] == '-')
		if(d == 1 || d == 3)
			d = 4 - d, add = 1;
	if(boa[x][y] == '|')
		if(d == 0 || d == 2)
			d = 2 - d, add = 1;
	if(boa[x][y] == '/')
		d = 3 - d, add = 1;
	if(boa[x][y] == '\\')
    {
		if(d == 2 || d == 3) d = 5 - d;
		else d = 1 - d;
		add = 1;
	}
	if(vis2[x][y][d] == os)	return;
	vis2[x][y][d] = os;
	po.push_back({x, y, d});
	if(add)
		pi.push_back({x, y});
	dfs2(x, y, d);
}
void solve()
{
	cin >> n >> m;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= m;j++)
			cin >> boa[i][j];
	for(int i = 1;i <= n;i++)
    {
		os++; dfs(i, 0, 0, 0);
		os++; dfs(i, m + 1, 2, 0);
	}
	for(int i = 1;i <= m;i++)
    {
		os++; dfs(0, i, 1, 0);
		os++; dfs(n + 1, i, 3, 0);
	}
	for(int i = 1;i <= n;i++)
    {
		for(int j = 1;j <= m;j++)
        {
			for(int k = 0;k < 4;k++)
            {
				if(dp[i][j][k]) continue;
				os++;
				dfs2(i, j, k);
				sort(pi.begin(), pi.end());
				pi.erase(unique(pi.begin(), pi.end()),pi.end());
				//cout << "i: " << i << " " << "j: " << j << " " << "k: " << k << endl;
				for(auto it : po)
                {
					dp[it.x][it.y][it.d] = pi.size();
					//cout << it.x << " " << it.y << " " << it.d << endl;
				}
				po.clear(); pi.clear();
			}
		}
	}
	int q; cin >> q;
	while(q--)
    {
		int x, y; string s;
		cin >> x >> y >> s;
		if(s[0] == 'a') cout << dp[x][y][3] << endl;
		if(s[0] == 'b') cout << dp[x][y][1] << endl;
		if(s[0] == 'r') cout << dp[x][y][0] << endl;
		if(s[0] == 'l') cout << dp[x][y][2] << endl;
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
	solve();
	return 0;
}
  • 30
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值