10 11模拟赛复盘

预期分数:40 + 100 + 0 + 10 = 150

实际得分: 0 + 100 + 0 + 10 = 110

反思or收获

1.在暴力没拿满的情况下不要死磕一道题!
2.只有乘法的运算才能用log去压,如果有加法一定不要想用log去压!

chess根据题意分析

在这里插入图片描述
在这里插入图片描述
如果暴力DP,发现字符串比较在最劣情况下为 O ( n ) O(n) O(n),显然过不了,考虑转化问题,如果在 第 1 1 1步就不是最优的策略一定不会被用到第 2 2 2,所以基于这个性质,我们可以考虑每一步最优为什么,将可以跑到最优的存入set,只用set里面的元素更新下一步的最优,这样的时间复杂度就转化为了 O ( ( n + m ) l o g ) O((n + m)log) O((n+m)log)

#include<bits/stdc++.h>
using namespace std;
const int MAX = 2100;
#define PII pair<int, int>
char ch[MAX][MAX];
char minn[MAX + MAX];
queue<PII> q;
set<PII> s[MAX + MAX];
int n, m;
int main() {
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++) 
		for(int j = 1; j <= m; j++) 
			cin>>ch[i][j];
	for(int i = 1; i <= n + m + 10; i++) minn[i] = 'z';
	minn[1] = ch[1][1];
	s[1].insert((PII){1, 1});
	for(int i = 2; i <= n + m; i++) {
		for(auto lst : s[i - 1]) {
			int x = lst.first, y = lst.second;
			if(x < n && minn[i] >= ch[x + 1][y]) {
				if(minn[i] > ch[x + 1][y]) s[i].clear();
				minn[i] = ch[x + 1][y];
				s[i].insert((PII){x + 1, y});
			} 
			if(y < m && minn[i] >= ch[x][y + 1]) {
				if(minn[i] > ch[x][y + 1]) s[i].clear();
				if(minn[i] > ch[x][y + 1]) minn[i] = ch[x][y + 1];
				s[i].insert((PII){x, y + 1});
			}
		}
	}
	for(int i = 1; i <= n + m - 1; i++) {
		cout<<minn[i];
	}
	return 0;
}

glass:简单装呀

在这里插入图片描述
在这里插入图片描述
数据范围,一眼状压,压什么? 我们发现一个瓶子只会被转移一次,且转移后一定不会再次转移,所以我们就压一个瓶子是否转移过即可,时间复杂度 O ( 2 n ∗ n ∗ n ) O(2^n*n*n) O(2nnn)跑不满,所以能过

//一个瓶子只会被转移 
#include<bits/stdc++.h>
using namespace std;
#define LL long long 
const int MAX = (1 << 21);
int n, k, ANS = 1e9;
int f[MAX], c[25][25];
int Minn[MAX][21];
int main() {
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	scanf("%d%d", &n, &k);
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < n; j++) {
			cin>>c[i][j];	
		}
	} 
	memset(Minn, 0x3f, sizeof(Minn));
	for(int i = 0; i <= (1 << n) - 1; i++) {
		for(int j = 0; j < n; j++) {
			if(i >> j & 1) continue;
			for(int k = 0; k < n; k++) {
				if(k != j) if((i >> k & 1) == 0) Minn[i][j] = min(Minn[i][j], c[j][k]); 
			}
		}
	}
	memset(f, 0x3f, sizeof(f));
	f[0] = 0;
	for(int i = 0; i <= (1 << n) - 1; i++) {
		for(int j = 0; j < n; j++) {
			if((i >> j & 1) == 0) {
				f[i | (1 << j)] = min(f[i | (1 << j)], f[i] + Minn[i][j]);	
			} 
		}
	}
	int ans = 1e9;
	for(int i = 0; i <= (1 << n); i++) {
		int sum = 0;
		for(int j = 0; j < n; j++) {
			if((i >> j & 1) == 0) sum++;
		}
		if(sum == k) ans = min(ans, f[i]);
	}
	cout<<ans<<endl;
	return 0;
}

card:组合意义的DP

在这里插入图片描述
在这里插入图片描述
原题,但是之前没做,考场上也是一筹莫展
因为一个序列不同是操作顺序有一位不同即可,所以一定有组合意义,考虑如何将一个问题转化为一个组合问题

在这里插入图片描述
随意构造一个序列,我们发现根据题意操作

1 1 1左侧的数下标一定递减,在 1 1 1右侧的数下标递增
那么最大值是怎么统计的呢? 一定为左侧数的单调递增加上右侧数的单调递增的个数

但是要规定左侧的最大值,小于右侧单调递增的最小值

左侧单调递增在原序列中为以某个数(x)的单调递减序列

为了保证上述规定,右侧的单调递增的数也由x为起点,这样的话将总数减1即为最大严格递增子序列的长度
而总数如何计算,假设对于 x x x而言由若干个组合可以构成最大严格递增子序列,对于其它的数除了1以外,往左放,往右放都无所谓,所以答案 + = s u m ∗ ( 2 n − l e n + 1 / 2 ) 1 +=sum*(2^{n-len+1}/2)1 +=sum(2nlen+1/2)1

而求最长上升子序列,最长下降子序列需要一个 ( l o g ) (log) (log)做法,离散化后值域线段树即可

//一个序列不同,当且仅当操作序列不同, 具有计数优势
//在1左边的下标递减, 值递增, 在1右边的下标递增, 值递增
//考虑用一个数划分阶段
#include<bits/stdc++.h>
using namespace std;
#define LL long long 
//#define int long long
const int MOD = 1e9 + 7;
const int MAX = 2e5 + 70;
int n, a[MAX], b[MAX], tot; 
LL num_up[MAX], num_down[MAX];
LL f_up[MAX], f_down[MAX];
int lsh[MAX];
struct node { int f, num; };
struct SegmentTree {
	int l, r;
	LL f, num;
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define f(x) tree[x].f
	#define num(x) tree[x].num
}tree[MAX * 4];
LL quick_mi(int x, int y) {
	LL xx = x, res = 1;
	while(y) {
		if(y & 1) res = res * xx % MOD;
		xx = xx * xx  % MOD;
		y = y / 2; 
	}
	return res;
}
void update(int p) {
	if(f(2 * p) > f(2 * p + 1)) { f(p) = f(2 * p); num(p) = num(2 * p) % MOD; }
	else if(f(2 * p + 1) > f(2 * p)) { f(p) = f(2 * p + 1); num(p) = num(2 * p + 1) % MOD; }
	else if(f(2 * p + 1) == f(2 * p)) { f(p) = f(2 * p); num(p) = (num(2 * p) + num(2 * p + 1)) % MOD; }
	return ;
}
void build(int p, int l, int r) {
	l(p) = l, r(p) = r, f(p) = num(p) = 0;
	if(l == r) { return ; }
	int mid = (l + r) >> 1;
	build(2 * p, l, mid);
	build(2 * p + 1, mid + 1, r);
}

node New(node x, node y) {
	if(x.f > y.f) return x;
	else if(y.f > x.f) return y;
	node NOW; NOW.f = x.f; NOW.num = (x.num + y.num) % MOD;
	return NOW;
}

node Find(int p, int l, int r) {
	if(l(p) >= l && r(p) <= r) {
		node NOW; NOW.f = f(p); NOW.num = num(p);
		return NOW;
	}	
	
	node NOW; NOW.f = 0, NOW.num = 0;
	int mid = (l(p) + r(p)) >> 1;
	if(l <= mid) NOW = New(NOW, Find(2 * p, l, r));
	if(r > mid) NOW = New(NOW, Find(2 * p + 1, l, r));
	return NOW;
}

void change(int p, int l, int r, int ff, int num) {
	if(l(p) == r(p)) {
		if(ff > f(p)) {
			f(p) = ff;
			num(p) = num % MOD;
		} else if(ff == f(p)) {
			num(p) = (num(p) + num) % MOD;
		}
		return ;
	}
	int mid = (l(p) + r(p)) >> 1;
	if(mid >= l) change(2 * p, l, r, ff, num);
	if(r > mid) change(2 * p + 1, l, r, ff, num);
	update(p); 
}

void Clear() { for(int i = 0; i <= 8e5 + 1; i++) tree[i].f = tree[i].num = 0; }

signed main() {
	freopen("c.in","r",stdin);
	scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &a[i]); b[i] = a[i];
	} 
	sort(b + 1, b + 1 + n);
	for(int i = 1; i <= n; i++) if(b[i] != b[i - 1]) lsh[++tot] = b[i];
	for(int i = 1; i <= n; i++) a[i] = lower_bound(lsh + 1, lsh + 1 + tot, a[i]) - lsh;
	reverse(a + 1, a + 1 + n);
	
	build(1, 0, 2e5 + 1);
	
	change(1, 2e5 + 1, 2e5 + 1, 0, 1);
	
	for(int i = 1; i <= n; i++) {
		node NOW = Find(1, a[i] + 1, 2e5 + 1);
		f_up[i] = NOW.f + 1;
		num_up[i] = NOW.num % MOD;
		change(1, a[i], a[i], f_up[i], num_up[i]);
	}
	
	Clear();
	
	change(1, 0, 0, 0, 1);
	for(int i = 1; i <= n; i++) {
		node NOW = Find(1, 0, a[i] - 1);
		f_down[i] = NOW.f + 1;
		num_down[i] = NOW.num % MOD;
		change(1, a[i], a[i], f_down[i], num_down[i]);
	}
	LL maxx = 0, NUM = 0;
	
	
	for(int i = 1; i <= n; i++) {
		if(f_up[i] + f_down[i] - 1 > maxx) { maxx = f_up[i] + f_down[i] - 1; NUM = 0; }
		if(f_up[i] + f_down[i] - 1 == maxx) NUM = (NUM + (1LL * num_up[i] * num_down[i] % MOD * quick_mi(2, n - f_up[i] - f_down[i] + 1) % MOD) ) % MOD;
	}
	cout<<maxx<<' '<<NUM<<endl;
	return 0;
}

godnumber:锅

在这里插入图片描述
在这里插入图片描述
ACAM套数位DP,还没打

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值