每日一题
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;
}

浙公网安备 33010602011771号