A层省选5

A. 卷王

赛时打表,把电脑搞炸两次

打表打了一个小时你敢信

这题先进行题意转换,发现如果\(t\)秒结束,相当于我们可以选择长度为\(t\)\(t - 1\) ......\(1\)的子串各一个进行反转

那么就可以进行\(DP\)了,设\(dp_{i,j}\)表示后\(i\)秒,能否到达状态\(j\),转移考虑反转哪个区间

code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
const int maxn = 100005;
ll n, l, r;
ll work(){
	if(l == r)return 1;
	int hi = 0;  for(int i = 63; i >= 0; --i)if(((1ll << i) & l) != ((1ll << i) & r)){hi = i; break;}
	int low = -1; for(int i = hi - 1; i >= 0; --i)if(r & (1ll << i)){low = i; break;}
	ll nans = (1ll << hi) - (l & ((1ll << hi) - 1)), rl = (1ll << hi) - nans;
	ll lr = (1ll << (low + 1)) - 1;
	nans = nans << 1;
	return nans + min(lr, rl - 1) + 1;
}
int main(){
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	int T = read();
	for(register int ask = 1; ask <= T; ++ask){
		n = read(), l = read(), r = read();
		if(n > 1)printf("%lld\n",work());
		else printf("%lld\n", r - l + 1);
	}
	return 0;
}

B. 赢王

首先考虑暴力处理\(l - r\)的最小操作次数,那么对于最左边的只能由右侧处理,我们操作次数就是\(min(x, k - x)\),然后下一个就变成最左侧了

所以\(n^3\)暴力就是这样

我们发现区间\(l ,r\)合法,当且仅当\(k | (sum_r - sum_{l - 1})\)结合上面的暴力可以发现上面的\(x\)就是\(sum_i - sum_{l - 1}\)

而且可以发现区间\(a,b\)合法操作次数为\(d\),区间\(b + 1, c\)合法操作次数为\(e\)那么区间\(a, c\)合法,操作次数为\(d + e\)

所以可以按照\(sum_i \% k\)\(i\)分类,统计一个区间被计算了多少次,大大优化了暴力

现在我们发现限制我们的主要是求教操作次数的部分,考虑使用主席树维护一下

具体的,对每个前缀维护\(sum == i\)的有多少,以及子树和,求解时根据不同值与\(sum_{l - 1}\)的大小关系分情况讨论即可

code
#include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<iostream>
#include<set>

using namespace std;
typedef pair<int, int> pii;
const int maxn = 1000005;
const int mod = 998244353;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c > '9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
	return x;
}
int root[maxn],n, k, a[maxn], sum[maxn], nxt[maxn], head[maxn], tot;
map<int, int>mp;
long long ans, del;
struct tree{
	#define ls t[x].l
	#define rs t[x].r
	struct node{int l, r, size, sum;}t[maxn * 28];
	int cnt;
	void push_up(int x){
		t[x].size = t[ls].size + t[rs].size;
		t[x].sum = (t[ls].sum + t[rs].sum) % mod;
	}
	void modify(int &x, int rt, int l, int r, int pos){
		if(!x) x = ++cnt, t[x] = t[rt];
		if(l == r){
			++t[x].size;
			t[x].sum += l;t[x].sum %= mod;
			return;
		}
		int mid = (l + r) >> 1;
		if(pos <= mid)t[x].l = 0, modify(t[x].l, t[rt].l, l, mid, pos);
		else t[x].r = 0, modify(t[x].r, t[rt].r, mid + 1, r, pos);
		push_up(x);
	}
	pii query(int x, int rt, int l, int r, int L, int R){
		if(!x || t[x].size - t[rt].size <= 0 || L > R)return pii(0, 0);
		if(L <= l && r <= R)return pii(t[x].sum - t[rt].sum, t[x].size - t[rt].size);
		int mid = (l + r) >> 1;
		pii ans = pii(0, 0);
		if(L <= mid){
			pii y = query(t[x].l, t[rt].l, l, mid, L, R);
			ans.first += y.first;
			ans.first %= mod;
			ans.second += y.second;
		}
		if(R > mid){
			pii y = query(t[x].r, t[rt].r, mid + 1, r, L, R);
			ans.first += y.first;
			ans.first %= mod;
			ans.second += y.second;
		}
		return ans;
	}
}t;

int work(int l, int r){
	long long nans = 0;
	int s = sum[l - 1], mk = k >> 1;
	if(s <= mk){
		pii x = t.query(root[r], root[l - 1], 0, k - 1, s + 1, s + mk);
		x.first = (x.first + mod) % mod;
		pii y = t.query(root[r], root[l - 1], 0, k - 1, 0, s - 1);
		y.first = (y.first + mod) % mod;
		pii z = t.query(root[r], root[l - 1], 0, k - 1, s + mk + 1, k - 1);
		z.first = (z.first + mod) % mod;
		nans += x.first - x.second * 1ll * s;
		nans += s * 1ll * y.second - y.first;
		nans += k * 1ll * z.second - (z.first - s * 1ll * z.second);
	}else{
		int ll = (s + mk) % k;
		pii x = t.query(root[r], root[l - 1], 0, k - 1, 0, ll);
		x.first = (x.first + mod) % mod;
		pii y = t.query(root[r], root[l - 1], 0, k - 1, ll + 1, s - 1);
		y.first = (y.first + mod) % mod;
		pii z = t.query(root[r], root[l - 1], 0, k - 1, s + 1, k - 1);
		z.first = (z.first + mod) % mod;
		nans += x.second * 1ll * (k - s) + x.first;
		nans += 1ll * y.second * s - y.first;
		nans += z.first - s * 1ll * z.second;
	}
	return nans % mod;
}
int main(){
	freopen("win.in","r",stdin);
	freopen("win.out","w",stdout);
	n = read(), k = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)sum[i] = (sum[i - 1] + a[i]) % k;
	t.modify(root[0], 0, 0, k - 1, 0);
	for(int i = 1; i <= n; ++i) t.modify(root[i], root[i - 1], 0, k - 1, sum[i]);
	mp[0] = 1; head[++tot] = 0;
	for(int i = 1; i <= n; ++i){
		if(mp[sum[i]])nxt[mp[sum[i]] - 1] = i;
		else head[++tot] = i;
		mp[sum[i]] = i + 1;
	}
	del = 1ll * n * (n + 1) / 2;
	for(int i = 1; i <= tot; ++i){
		int s = 0, now = head[i];
		do{
			++s;
			now = nxt[now];
		}while(now);
		del -= (1ll * s * (s - 1) / 2);
		int pl = 0; now = head[i];
		while(nxt[now]){
			++pl;
			ans += 1ll * pl * (s - pl)  % mod * work(now + 1, nxt[now]) % mod;
			now = nxt[now];
		}
	}
	ans = ((ans - del) % mod + mod) % mod; 
	printf("%lld\n",ans);
	return 0;
}

C. 稳王

题解讲的很详细了

最优策略是能秒掉\(boss\)再出牌

求杀死\(boss\)的期望就是求杀不死\(boss\)的期望 + 1

然后分情况讨论

只有毒药/复读/火球,有两个,有三个的情况

具体题解讲的很详细了,,,,(主要是懒的打了)

image

那个合式证明设\(s = \sum\),用\(xs - s\)就行

注意一点就是矩阵的部分先认为第一张也有伤害,最后减去不合法的方案即可

代码还有一点小注释

code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<iostream>
#include<set>

using namespace std;
typedef long long ll;
const int mod = 998244353;
const int inv2 = 499122177, inv3 = 332748118;
const int maxn = 100;
ll qpow(ll x, ll y){
	y %= (mod - 1);
	ll ans = 1;
	for(; y; y >>= 1, x = x * x % mod)if(y & 1)ans = ans * x % mod;
	return ans;
}
struct matrix{
	ll a[5][5]; int n;
	matrix(){memset(a, 0, sizeof(a));}
	void clear(){memset(a, 0, sizeof(a));}
	matrix operator * (matrix x) const{
		matrix ans; ans.n = n;
		for(int i = 0; i < n; ++i)
			for(int k = 0; k < n; ++k)
				for(int j = 0; j < n; ++j)
					ans.a[i][j] = (ans.a[i][j] + a[i][k] * x.a[k][j]) % mod;
		return ans;
	}
};
matrix qpow(matrix x, ll y){
	matrix ans; ans.n = x.n;
	for(int i = 0; i < ans.n; ++i)ans.a[i][i] = 1;
	for(; y; y >>= 1, x = x * x)if(y & 1)ans = ans * x;
	return ans;
}
ll clac(ll x, ll n){
	// \sum_{i = 1}^{n} x^i = x * (x ^ n - 1) / (x - 1)
	//这里是求 \sum_{i = 1}^n inv_x^i
	return 1ll * x * (1 - qpow(x , n)) % mod * qpow(1 - x + mod, mod - 2) % mod;
}
int main(){
	freopen("stable.in","r",stdin);
	freopen("stable.out","w",stdout);
	int t; scanf("%d",&t);
	for(int i = 1; i <= t; ++i){
		ll n; scanf("%lld",&n);
		ll m = (n - 1) / 2, ans = 0;
		
		//只有复读
		ans += inv2;
		
		//只有毒药
		ans += inv2 * ((1 - qpow(inv3, n) + mod) % mod) % mod;
		
		//只有火球
		ans += inv2 * ((1 - qpow(inv3, m) + mod) % mod) % mod;
		
		//火球 + 复读
		ans += 2 * (1 - qpow(inv3 * 2, m) + mod) % mod - (1 - qpow(inv3, m));
		ans = (ans % mod + mod) % mod;
		// cerr << ans << endl;
		
		//注意我们实际上统计杀不死的情况的贡献,下面矩阵都加上了前n项,其实是前n-1项(因为有一张毒药没有伤害)
		
		//复读 + 毒药
		/*
			先默认都有伤害,再去掉不合法的

			[s , s - 1, sum]

			inv3, 1, inv3
			inv3, 0, inv3
			0, 0, 1

			sum 为 s 项的和,(2,2) 之前的和, 其他系数与S 一致,相当于加S
		*/
		matrix a, b;
		a.n = b.n = 3;
		a.a[0][0] = 1;
		b.a[0][0] = b.a[1][0] = b.a[0][2] = b.a[1][2] = inv3;
		b.a[0][1] = b.a[2][2] = 1;
		a = a * qpow(b, n);
		ans = (ans + a.a[0][2]) % mod;
		ans = (ans - clac(inv3, n / 2) - clac(inv3, n)) % mod;
		//减去全是复读(默认伤害2,实际没有伤害),全是毒药(不在这里统计)
		ans = (ans % mod + mod) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;
		
		// 毒药 + 火球
		//类似上面
		a.n = b.n = 4;
		a.a[0][0] = 1;
		b.a[0][0] = b.a[0][3] = b.a[2][0] = b.a[2][3] = inv3;
		b.a[0][1] = b.a[1][2] = b.a[3][3] = 1;
		a = a * qpow(b, n);
		ans = (ans + a.a[0][3]) % mod;
		ans = (ans - clac(inv3, n) - clac(inv3, n / 3)) % mod;
		ans = (ans % mod + mod) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;

		//毒药 + 火球 + 复读
		//需要容斥一下
		//step1 : + 都有 伤害; 1  3  4
		a.n = b.n = 5;
		a.a[0][0] = 1;
		b.a[0][0] = b.a[0][4] = b.a[2][0] = b.a[2][4] = b.a[3][0] = b.a[3][4] = inv3;
		b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
		a = a * qpow(b, n);
		ans = (ans + a.a[0][4]) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;

		// step2 : - 毒药 + 火球
		a.a[0][0] = 1;
		b.a[0][0] = b.a[0][4] = b.a[2][0] = b.a[2][4] = inv3;
		b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
		a = a * qpow(b, n);
		ans = (ans - a.a[0][4] + mod) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;

		// step3 : - 毒药 + 复读
		a.a[0][0] = 1;
		b.a[0][0] = b.a[0][4] = b.a[3][0] = b.a[3][4] = inv3;
		b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
		a = a * qpow(b, n);
		ans = (ans - a.a[0][4] + mod) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;


		// step 4 : - 火球 + 复读
		a.a[0][0] = 1;
		b.a[2][0] = b.a[2][4] = b.a[3][0] = b.a[3][4] = inv3;
		b.a[0][1] = b.a[1][2] = b.a[2][3] = b.a[4][4] = 1;
		a = a * qpow(b, n);
		ans = (ans - a.a[0][4] + mod) % mod;
		a.clear(), b.clear();
		// cerr << ans << endl;

		// step 5 + 全是火球/复读/毒药
		ans = (ans + clac(inv3, n) + clac(inv3, n / 3) + clac(inv3, n / 4)) % mod;
		
		//记得 + 1
		printf("%lld\n",(ans + 1 + mod) % mod);
	}
	return 0;
}

posted @ 2022-08-18 07:07  Chen_jr  阅读(31)  评论(0编辑  收藏  举报