2024牛客暑期多校训练营1

A-Bit Common

题意

给定两个整数 n n n m m m,在所有包含 n n n 个非负整数且每个整数小于 2 m 2^m 2m 的序列中,你需要统计其中有多少个序列 A A A 存在一个非空的子序列,其所有元素按位与的结果为 1。

注意,序列 A A A 的非空子序列是可以通过删除 A A A 的零个或多个元素并按原顺序排列剩余元素得到的非空序列。

由于答案可能非常大,请输出对一个正整数 q q q 取模的结果。

思路

思维。为了使非空子序列的 A N D AND AND 和为 1 1 1 ,子序列中的数的二进制最低位必须为 1 1 1 。可以枚举子序列的个数,保证子序列之外的数的二进制最低位为 0 0 0,枚举这些数。对于二进制最低位为 1 1 1 的数,设此时有 i i i 个,为了这些数的 A N D AND AND 和为 0 0 0,要保证这些数最低位的位置全为 1 1 1 ,其他 m − 1 m-1 m1 个位置上,每一个位置至少有一个 0 0 0,对于这部分的数,此时的情况和为 ( 2 i − 1 − 1 ) m − 1 {(2^{i - 1} - 1)}^{m - 1} (2i11)m1;对于其他二进制最低位为 0 0 0 n − i n - i ni 个数,其他 m − 1 m - 1 m1 个位置随意,情况数为 2 ( n − i ) ( m − 1 ) 2^{(n - i)(m - 1)} 2(ni)(m1)。对于一个 i i i,总情况数为 C n i × 2 ( n − i ) ( m − 1 ) × ( 2 i − 1 − 1 ) m − 1 C_n^i \times2^{(n - i)(m - 1)} \times {(2^{i - 1} - 1)}^{m - 1} Cni×2(ni)(m1)×(2i11)m1,最终答案 a n s = ∑ i = 1 n C n i × 2 ( n − i ) ( m − 1 ) × ( 2 i − 1 − 1 ) m − 1 ans=\sum_{i=1}^nC_n^i \times2^{(n - i)(m - 1)} \times {(2^{i - 1} - 1)}^{m - 1} ans=i=1nCni×2(ni)(m1)×(2i11)m1

代码

#include <bits/stdc++.h>

#define int long long
#define endl '\n'

using namespace std;

const int N = 5e3 + 3;
int C[N][N];

int qpow(int a, int n, int mod) {
    int ans = 1;
    while(n) {
        if(n & 1) ans = ans * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ans % mod;
}

void solve() {
    int n, m, q;
    cin >> n >> m >> q;
    C[0][0] = 1;
    for(int i = 1; i <= n; i++){
        for(int j = 0; j <= i; j++){
            if(j == 0){
                C[i][j] = 1 % q;
            }
            else{
                C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % q;
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= n; i++) {
        ans = (ans + C[n][i] * qpow(2, (n - i) * (m - 1), q) % q * qpow((qpow(2, i, q) - 1) % q, m - 1, q) % q) % q;
    }
    cout << ans << endl;
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T = 1;
    //cin >> T;
    while(T--) {
        solve();
    }
    return 0;
}

C-Sum of Suffix Sums

题意

给定一个初始为空的数组,需要执行 q q q 次操作:
对于给定的两个非负整数 t t t v v v,从数组末尾连续取出 t t t 次元素,然后将 v v v 添加到数组的末尾。保证在此操作之前, t t t 不会超过数组的长度。

在每次操作后,设 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 是当前数组,找到 s 1 , s 2 , … , s n s_1, s_2, \ldots, s_n s1,s2,,sn 的和,其中 s i = a i + a i + 1 + … + a n s_i = a_i + a_{i+1} + \ldots + a_n si=ai+ai+1++an 是从位置 $ i $ 开始的后缀的和。

输出它们模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的结果。

思路

用栈模拟。对于一对 t t t v v v,记此时取出的元素为 x x x,为本次操作取出的第 i d x idx idx个元素,栈内元素的个数为 c n t cnt cnt,所有元素的后缀和为 a n s ans ans,取出这个元素对于 a n s ans ans的贡献为 − x ∗ ( c n t − i d x ) -x * (cnt - idx) x(cntidx);取出 t t t 个元素后并加入一个元素的元素个数为 c n t − t + 1 cnt - t + 1 cntt+1,加入元素 v v v 对答案的贡献为 + c n t ∗ v + cnt * v +cntv

代码

#include <bits/stdc++.h>

#define int long long
#define endl '\n'

using namespace std;

const int mod = 1e9 + 7;

signed main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int q = 1, ans = 0, cnt = 0;
	cin >> q;
	stack<int> s;
	while(q--) {
		int t, v;
		cin >> t >> v;
		for(int i = 0; i < t; i++) {
			int pos = s.top();
			s.pop();
			ans -= (cnt - i) * pos;
			ans = (ans % mod + mod) % mod;
		}
		cnt++;
		cnt -= t;
		s.push(v);
		ans = (ans + v * cnt) % mod;
		cout << ans << endl;
	}
	return 0;
}

H-World Finals

题意

l z r 010506 lzr010506 lzr010506 拥有参加两场比赛的资格,但是只能参加一场比赛。其他学校的中的部分也拥有参加两场比赛的资格,但是 l z r 010506 lzr010506 lzr010506 可以替他们决定参加哪场比赛。问 l z r 010506 lzr010506 lzr010506 能拿到的最高名次是多少。

比赛排名的依据为过题数量和罚时,过题越多名次越靠前;相同过题数罚时越少名次越靠前。

思路

模拟 + + + 贪心。得到正确的比赛排名后,在一场比赛中,若有参加两场比赛资格的学校名次在 l z r 010506 lzr010506 lzr010506 前,则把此学校移到另外一场比赛。答案取最小值。

代码

#include "bits/stdc++.h"

#define int long long
#define endl '\n'

using namespace std;

struct Team {
    string s;
    int num, pen;
};

bool cmp(const Team& a, const Team& b) {
    if (a.num == b.num)
        return a.pen < b.pen;
    return a.num > b.num;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<Team> v1(n);
    for (int i = 0; i < n; ++i) {
        cin >> v1[i].s >> v1[i].num >> v1[i].pen;    
    }
    sort(v1.begin(), v1.end(), cmp);
    int m;
    cin >> m;
    vector<Team> v2(m);
    for (int i = 0; i < m; ++i) {
        cin >> v2[i].s >> v2[i].num >> v2[i].pen;
    }
    sort(v2.begin(), v2.end(), cmp);
    int ans1 = 1e10, ans2 = 1e10;
    set<string> cnt;
    for (int i = 0; i < n; ++i) {
        if (v1[i].s == "lzr010506") {
            ans1 = i + 1;
            break;
        } else {
            cnt.insert(v1[i].s);
        }
    }
    for (int i = 0; i < m; ++i) {
        if (cnt.count(v2[i].s)) {
            --ans1;
        }
    }
    cnt.clear();
    for (int i = 0; i < m; ++i) {
        if (v2[i].s == "lzr010506") {
            ans2 = i + 1;
            break;
        } else {
            cnt.insert(v2[i].s);
        }
    }
    for (int i = 0; i < n; ++i) {
        if (cnt.count(v1[i].s)) {
            --ans2;
        }
    }
    int ans = min(ans1, ans2);
    cout << ans << endl;
    return 0;
}

I-Mirror Maze

题意

有一个 n × m n \times m n×m 的镜子迷宫,每个格子里都有一面镜子。这些镜子有以下四种类型之一:

  • -,从上方或下方来的光会被反射回去,从左边或右边来的光会继续向前,不会被反射。
  • |,从左边或右边来的光会被反射回去,从上方或下方来的光会继续向前,不会被反射。
  • /,从左、右、上、下来的光分别会被反射到上、下、左、右。
  • \,从左、右、上、下来的光分别会被反射到下、上、右、左。

现在有 q q q 个光源。对于每个光源,发出的光在足够长的时间内会被多少个不同的镜子反射。

思路

模拟 + + + 记忆化搜索。可以抽象成找环和链,每一个环和链都是唯一的。记忆化搜索环,链不用记忆化搜索。用数字来表示不同状态: d x i dx_i dxi d y i dy_i dyi ( 0 ⩽ i ⩽ 3 ) (0 \leqslant i \leqslant 3) (0i3) 表示光线坐标移动状态; a n s i j k ans_{ijk} ansijk表示坐标为 ( i , j ) (i,j) (i,j)的镜子第 k k k种状态的答案; v i s i j k vis_{ijk} visijk记录一次 d f s dfs dfs搜索到的点。具体见代码注释。

代码

#include <bits/stdc++.h>

#define int long long
#define endl '\n'

using namespace std;

const int N = 1e3 + 3;

// mp为镜子阵    ans维护镜子阵每个点不同方向射入的结果    cir记录是否成环    vis记录节点是否被遍历
int mp[N][N], ans[N][N][4], cir[N][N][4], vis[N][N][4];

// 0 <-> '|'    1 <-> '-'    2 <-> '/'    3 <-> '\'
int to[4][4]={0, 1, 3, 2, 1, 0, 2, 3, 3, 2, 1, 0, 2, 3, 0, 1};
// 0 <-> above    1 <-> below    2 <-> left    3 <-> right
int dx[4]={-1, 1, 0, 0};
int dy[4]={0, 0, -1, 1};

int n, m, q;

int dfs(int x, int y, int dir, int cnt) {
	if(x < 1 || x > n || y < 1 || y > m) {
		return 0;
	}
	if(vis[x][y][dir]) {
		cir[x][y][dir] = 1;
		return cnt;
	}
	if(cir[x][y][dir]) {
		return ans[x][y][dir];
	}
	int tow = to[mp[x][y]][dir];

	int f = 1;
	for(int i = 0; i < 4; i++) {
		if(vis[x][y][i]) {
			f = 0;
			break;
		}
	}
	if(f && tow != dir) {
		f = 1;
	} else {
		f = 0;
	}

	if(tow != dir) vis[x][y][dir] = 1;
	int xx = x + dx[tow], yy = y + dy[tow], towl = dfs(xx, yy, tow, cnt + f);
	if(tow != dir) vis[x][y][dir] = 0;
	if(cir[xx][yy][tow]) {
	    ans[x][y][dir] = towl;
	    cir[x][y][dir] = 1;
  	}
  	else ans[x][y][dir] = towl + f;
  	return ans[x][y][dir];
}

void solve() {
	cin >> n >> m;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= m; j++) {
			char x;
			cin >> x;
			if(x == '|') {
				mp[i][j] = 0;
			} else if(x == '-') {
				mp[i][j] = 1;
			} else if(x == '/') {
				mp[i][j] = 2;
			} else {
				mp[i][j] = 3;
			}
		}
	}
	cin >> q;
	for(int i = 1; i <= q; i++) {
		int x, y, dir;
		string s;
		cin >> x >> y >> s;
		if(s[0] == 'a') {
			dir = 0;
		} else if(s[0] == 'b') {
			dir = 1;
		} else if(s[0] == 'l') {
			dir = 2;
		} else {
			dir = 3;
		}
		cout << dfs(x + dx[dir], y + dy[dir], dir, 0) << endl;
	}
}

signed main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int T = 1;
	//cin >> T;
	while(T--) {
		solve();
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值