每日一题

题单


P14367

我们考虑一个 \(dp_{i,j}\) 表示目前考虑到前 \(i\) 行,然后还有 \(j\) 列一个帐篷都没放。

然后我们考虑到一个性质就是一行只能放小于等于二个帐篷,我们分类:

  • 对于一个都不放:\(dp_{i - 1,j} \to dp_{i, j}\)
  • 对于只放一个(包含放一对 \(N,S\)): \(dp_{i - 1, j} \times 4 \times j \to dp_{i, j - 1} \ , \ dp_{i - 2, j} \times j \times (i - 1) \to dp_{i, j - 1}\),含义分别为放单个 \(E,S,W,N\) 和放一对 \(S,N\)
  • 对于放一对 \(E,W\)\(dp_{i - 1, j} \times \frac{ j \times (j - 1)}{2} \to dp_{i, j - 2}\)

于是就做完了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f=1,t=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t*f;}inline void write(int x){if(x<0){putchar('-');x=-x;}if(x>=10){write(x/10);}putchar(x%10+'0');}}
using namespace io;
int n, m;
const int N = 3010, MOD = 1e9 + 7;
int dp[N][N];
int C[N][N];
signed main() {
#ifndef Air
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n = read();
	m = read();
	C[0][0] = 1;
	for(int i = 1; i < N; i++){
		C[i][0] = 1;
		for(int j = 1; j <= i; j++){
			C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
			C[i][j] %= MOD;
		}
	}
	dp[0][m] = 1;
	for(int i = 1; i <= n; i++){
		for(int j = 0; j <= m; j++){//剩余 j 列是空白的
			if(j >= 1){// 只放 1 个
				dp[i][j - 1] += dp[i - 1][j] * 2 * j % MOD;// 放 E,W
				dp[i][j - 1] %= MOD;
				dp[i][j - 1] += dp[i - 1][j] * 2 * j % MOD;// 只放 1 个 S,N
				dp[i][j - 1] %= MOD;
				if(i >= 2)
				dp[i][j - 1] += dp[i - 2][j] * j * (i - 1) % MOD;//放一对 S,N
				dp[i][j - 1] %= MOD;
			}
			if(j >= 2){// 放 2 个
				dp[i][j - 2] += dp[i - 1][j] * C[j][2] % MOD;//放一对 E,W
				dp[i][j - 2] %= MOD;
			}
			dp[i][j] += dp[i - 1][j];//一个不放
			dp[i][j] %= MOD;
		}
	}
	int ans = 0;
	for(int i = 0; i < m; i++){
		ans += dp[n][i];
		// cerr << dp[n][i] << '\n';
		ans %= MOD;
	}
	cout << ans;
	return 0;
}

P14300

我们考虑一个问题,假如说我现在已经确定拿哪一些物体了,那么最小移动步数是好算的,根据这个我们设计一个 \(dp_{i,j,k}\) 的状态,表示前 \(i\) 个,目前装了 \(j\) 个,花费步数 \(k\) 步。

我们考虑把第二维给踢了,我们预处理一个东西表示只装 \(l, r\) 装满一车的最大权值,有如下转移:

\[dp_{i, j} = \max \{dp_{k, j - 2(i - 1)} + val(k + 1, i) \} \]

猜这东西是有决策单调性的,做完了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f=1,t=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t*f;}inline void write(int x){if(x<0){putchar('-');x=-x;}if(x>=10){write(x/10);}putchar(x%10+'0');}}
using namespace io;
int n, m, d;
const int N = 460;
int a[N];
//我们考虑dp
//一个想法是 dp_i_j_k
//但是时间是 n^4
//我想把第2维给踢了
//价值函数也是好做的
//dp_i_j  考虑到前 i 个,走了 j 步
//dp_i_j = max(dp_{k - 1}_{j - 2 * i} + val(k, j))
//考虑优化
//猜一手决策单调性
int dp[N][N * N];
int val[N][N];
int ans = 0;
void solve(int f1, int l, int r, int L, int R){
	if(l > r) {
		return ;
	}
	if(L > R) return ;
	int mid = (l + r) >> 1;
	int npos = 0;
	for(int id = L; id <= min(mid, R); id++){
		if(dp[id - 1][f1] + val[id][mid] > dp[mid][f1 + (mid - 1) * 2]){
			dp[mid][f1 + (mid - 1) * 2] = dp[id - 1][f1] + val[id][mid];
			npos = id;
		}	
	}
	if(f1 + (mid - 1) * 2 <= d)
	ans = max(ans, dp[mid][f1 + (mid - 1) * 2]);
	solve(f1, l, mid - 1, L, npos);
	solve(f1, mid + 1, r, max(npos, L), R);
}
signed main() {
#ifndef Air
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n = read();
	m = read();
	d = read();
	int lim = (n * n * 2) / m;
	// memset(dp, -0x3f, sizeof dp);
	int tot = 0;
	for(int i = 2; i <= n; i++){
		a[i] = read();
		tot += a[i];
	}
	if(d > lim){
		cout << tot << '\n';
		return 0;
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= d; j++){
			dp[i][j] = -0x3f3f3f3f3f3f3f3f;
		}
	}
	for(int i = 1; i <= n; i++){
		multiset<int> st;
		int tmp = 0;	
		for(int j = i; j <= n; j++){
			if((int)st.size() < m){
				st.insert(a[j]);
				tmp += a[j];
			}
			else{
				if(a[j] > (*st.begin())){
					tmp -= (*st.begin());
					st.erase(st.begin());
					st.insert(a[j]);
					tmp += a[j];
				}
			}
			val[i][j] = tmp;
		}
	}
	dp[1][0] = 0;
	for(int nt = 0; nt <= d; nt++){
		solve(nt, 1, n, 1, n);
	}
	// int ans = 0;
	// for(int i = 1; i <= n; i++){
	// 	for(int j = 0; j <= d; j++){
	// 		ans = max(ans, dp[i][j]);
	// 	}	
	// }
	cout << ans;
	return 0 ;
}

P10856

看题解了,自己想没有想到对于线段树一个节点最多只有 \(O(len)\) 的答案不同的 \(x\),直接存就行。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define uint unsigned long long
#define double long double
#define Air
namespace io{inline int read(){int f=1,t=0;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t*f;}inline void write(int x){if(x<0){putchar('-');x=-x;}if(x>=10){write(x/10);}putchar(x%10+'0');}}
using namespace io;
//我们考虑什么时候异或完有贡献?
//XXXXX01...1 -> XXXXX10...0
//我们对于一个值,我们考虑枚举他异或玩后最低位 0 是谁
//然后其异或的值我就能确定一个后缀
//我们前后缀反转,那么就对应一个区间
//然后做完了?
//寄了
//不好做区间查询,需要二维数点了
//只会离线cdq,可他强制在线
//去看tj了
//tj太牛了
int MOI;
int t, n, q;
const int N = 3e5 + 10;
int a[N];
// struct Segment{
// 	int l, r;
// 	int dat;
// 	int laz;
// }seg[N * 4];
// void upd(int p){
// 	seg[p].dat = max(seg[p * 2].dat, seg[p * 2 + 1].dat);
// }
// void spread(int p){
// 	seg[p * 2].dat += seg[p].laz;
// 	seg[p * 2].laz += seg[p].laz;
// 	seg[p * 2 + 1].dat += seg[p].laz;
// 	seg[p * 2 + 1].laz += seg[p].laz;
// 	seg[p].laz = 0;
// }
// void build(int p, int l, int r){
// 	seg[p].l = l;
// 	seg[p].r = r;
// 	seg[p].dat = seg[p].laz = 0;
// 	if(l == r){
// 		return ;
// 	}
// 	int mid = (l + r) >> 1;
// 	build(p * 2, l, mid);
// 	build(p * 2 + 1, mid + 1, r);
// }
// void change(int p, int l, int r){
// 	if(seg[p].l >= l && seg[p].r <= r){
// 		seg[p].dat ++;
// 		seg[p].laz ++;
// 		return ;
// 	}
// 	spread(p);
// 	int mid = (seg[p].l + seg[p].r) >> 1;
// 	if(l <= mid){
// 		change(p * 2, l, r);
// 	}
// 	if(r > mid){
// 		change(p * 2 + 1, l, r);
// 	}
// 	upd(p);
// }
// int ask(int p, int x){
// 	if(seg[p].l == seg[p].r) {
// 		return seg[p].dat;
// 	}
// 	spread(p);
// 	int mid = (seg[p].l + seg[p].r) >> 1;
// 	if(x <= mid){
// 		return ask(p * 2, x);
// 	}
// 	else{
// 		return ask(p * 2 + 1, x);
// 	}
// }
// int reve(int x){
// 	int tmp = 0;
// 	for(int i = 0; i < t; i++){
// 		if(x & (1ll << (i))){
// 			tmp += (1ll << (t - i));
// 		}
// 	}
// 	return tmp;
// }
struct Data{
	int l, r;
	int len;
	friend Data operator + (Data x, Data y){  
		if(!y.len) return x;
		if(!x.len) return y;
		return {x.l, y.r, x.len + y.len - (x.r == y.l)};
	}
};
struct Segment{
	int l, r;
	vector<Data>dat;
}seg[N * 4];
void upd(int p){
	int len = seg[p].r - seg[p].l + 1;
	for(int i = 0; i < len / 2; i++){
		seg[p].dat[i] = seg[p * 2].dat[i] + seg[p * 2 + 1].dat[i];
	}
	for(int i = len / 2; i < len; i++){
		seg[p].dat[i] = seg[p * 2 + 1].dat[i ^ (len / 2)] + seg[p * 2].dat[i ^ (len / 2)];
	}
}
void build(int p, int l, int r){
	int len = r - l + 1;
	// cerr << p << ' ' << l << ' ' << r << '\n';
	seg[p].l = l;
	seg[p].r = r;
	seg[p].dat.resize(len + 10);
	if(l == r){
		seg[p].dat[0] = {a[l], a[r], 1};
		return ;
	}
	int mid = (l + r) >> 1;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r);
	upd(p);
}
Data ask(int p, int l, int r, int x, int dep){
	// cerr << p << ' ' << l << ' ' << r << ' ' << x << ' ' << dep << '\n';
	if(seg[p].l > r || seg[p].r < l || l > r) return {0, 0, 0};
	if(seg[p].l >= l && seg[p].r <= r){
		// cerr << seg[p].dat[x & ((1ll << (dep + 1)) - 1)].l << ' ' << seg[p].dat[x & ((1ll << (dep + 1)) - 1)].r << ' ' << seg[p].dat[x & ((1ll << (dep + 1)) - 1)].len << '\n';	
		return seg[p].dat[x & ((1ll << (dep + 1)) - 1)];
	}
	int mid = (seg[p].l + seg[p].r) >> 1;
	if(x & (1ll << dep)){
		return ask(p * 2 + 1, mid + 1 + l - seg[p].l, mid + 1 + min(mid, r) - seg[p].l, x, dep - 1) + ask(p * 2, seg[p].l + max(mid + 1, l) - (mid + 1), seg[p].l + r - (mid + 1), x, dep - 1);
	}
	else{
		return ask(p * 2, l, r, x, dep - 1) + ask(p * 2 + 1, l, r, x, dep - 1);
	}
}
signed main() {
#ifndef Air
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
#endif
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	MOI = read();
	t = read();
	n = (1ll << t) - 1;
	q = read();
	if(t == 0){
		read();
		while(q--){
			int op = read();
			if(op == 1){
				read();
			}
			if(op == 2){
				read();
				read();
				cout << 1 << '\n';
			}
		}
		return 0;
	}
	for(int i = 0; i <= n; i++){
		a[i] = read();	
	}
	build(1, 0, n);
	int last = 0;
	int now = 0;
	while(q--){
		int op = read();
		if(op == 1){
			int x = (read() ^ (MOI * last));
			now ^= x;
		}
		else{
			int l = (read() ^ (MOI * last)), r = (read() ^ (MOI * last));
			if(l > r) swap(l, r);
			last = ask(1, l, r, now, t - 1).len;
			cout << last << '\n';
		}
	}
	return 0;
}
posted @ 2025-11-03 19:44  Air2011  阅读(12)  评论(0)    收藏  举报