10 25模拟赛总结

7:40 - 8:00 : 看T1,然后想到了二分做法
8:00 - 8:30 : 尝试证明贪心,发现是对的,然后码完了
8:30 - 8:40 : 看T2, 发现和之前一道计数题很像,但是计数太差了,考虑先跳过回来再看
8:40 - 9:20 :看T3, 发现貌似不太会,尝试性质,好像可以骗一下分?
9:20 - 9:30 : 看T4, 貌似有点头绪,然后就接着想
9:30 - 9:35 : 上了个厕所,目前得分还只是100分,决定了先打T4,然后T3,最后看T2
9:35 - 9:50 : 想到了一个T4的假做法,然后码不出来了
9:50 - 10:40 : 直接冲trajan,然后发现码量优秀,幸好没有什么细节
10:40 - 11:20 : 疯狂调试,然后调出来了样例
11:20 - 11:40 : 尝试优化一下,但是没有什么结果

期望得分 : 100 + 0 + 10 + 55 = 165
实际得分 : 100 + 0 + 0 + 52 = 152

问题or收获?

1.对于T1,并不能快速切掉无法对后面题目建立优势,今天是运气好调试出来了
2.鸡蛋要放在多个篮子里,如果今天T4部分分挂了,那么T2,T3一分不打直接寄完了
3.比赛过程中一直有着"这是模拟赛,我就死磕T4了的念头",如果是noip还能有这样的心态吗?但是就结果而言貌似是有效的,
但实际呢?T4的暴力其实根本不用花费两个小时的时间,如果快点打的话最多一个小时就可以打完了,其实还是自己对待模拟赛的态度不端正吧

订题的态度太懈怠了
题目订正:(10.25号结束前补完)

项链

在这里插入图片描述
在这里插入图片描述
二分答案,然后 c h e c k check check,如果大于 m i d mid mid,删除较大的数,正确性反证法

 #include<bits/stdc++.h>
using namespace std;
#define LL long long 
const int MAX = 5e5 + 70;
LL tid, n, m, sk[MAX], tot;
LL a[MAX];
struct made {
	LL val;
	int id;
}b[MAX];
bool mycmp(made X, made Y) {  return X.val < Y.val;  }
bool check(LL Max) {
	tot = 0;
	LL use = 0;
	for(int i = 1; i <= n; i++) {
		if(!tot || sk[tot] + a[i] <= Max) {
			sk[++tot] = a[i];
		} else {
			bool q = 0;
			while(tot && sk[tot] + a[i] > Max) {
				if(a[i] > sk[tot]) {
					q = 1; use++;
					break;
				} else { tot--; use++; }
			}
			if(q == 1) continue;
			else sk[++tot] = a[i];
		}
	}
	if(use > (n - m)) return 0;
	int r = tot, l = 1;
	while(l < r) {
		if(sk[r] + sk[l] > Max){
			if(sk[r] > sk[l]) { r--; use++; } 
			else{ l++; use++; }
		} else break;	
	}
	if(use <= (n - m)) return 1;
	return 0;
}
int main() {
	freopen("necklace.in","r",stdin);
	freopen("necklace.out","w",stdout);
	scanf("%lld%lld%lld", &tid, &n, &m);
	for(int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
		b[i].val = a[i]; b[i].id = i;
	}
	sort(b + 1, b + 1 + n, mycmp);
	
	if(m == 2) {
		printf("%lld\n", b[1].val + b[2].val);
		return 0;
	} 
	else if(m == 3) {
		printf("%lld\n", b[2].val + b[3].val);
		return 0;
	} 
	else {
		LL l = 0, r = 2e9, ans;
		while(l <= r) {
			LL mid = (l + r) >> 1;
//			printf("mid %d\n", mid);
			if(check(mid)) {
				r = mid - 1;
				ans = mid;
			} else {
				l = mid + 1;
			}
		}
		printf("%lld\n", ans);
		return 0;
	}
	return 0;
}

计树

在这里插入图片描述
在这里插入图片描述
计数好题:看看数据范围,发现希望得到一个 n 3 n^3 n3的做法
如果有左右儿子不同,我们就不必在意树的形态,只用考虑可以给那些节点当儿子与树数了
如何设计状态, 第一维肯定要枚举当前是那个节点,第二维发现转移时可能会出现森林,故为有多少棵树,第三维考虑还有多少个空位可以放
故设计 D P DP DP状态为: f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示到 i i i号节点,有 j j j棵树,有 k k k个空位可以放的防范书
发现题目限制,至少有一个儿子的编号大于该节点的编号,考虑倒序 D P DP DP然后大力分类讨论
若分配0个儿子: f [ i + 1 ] [ j + 1 ] [ k ] + = f [ i ] [ j ] [ k ] f[i + 1][j + 1][k] += f[i][j][k] f[i+1][j+1][k]+=f[i][j][k]不增加空位,增加一颗树
若分配1个儿子:
\qquad <1>.不分配父亲: f [ i + 1 ] [ j ] [ k ] + = f [ i ] [ j ] [ k ] ∗ j ∗ 2 f[i + 1][j][k] += f[i][j][k] * j * 2 f[i+1][j][k]+=f[i][j][k]j2
\qquad <2>. 分配父亲: f [ i + 1 ] [ j − 1 ] [ k ] + = f [ i ] [ j ] [ k ] ∗ k ∗ ( j − 1 ) ∗ 2 f[i +1][j - 1][k] += f[i][j][k] * k * (j - 1) * 2 f[i+1][j1][k]+=f[i][j][k]k(j1)2
若分配2个儿子
\qquad <1>.给1个儿子,提供一个空位,不给父亲: f [ i + 1 ] [ j ] [ k + 1 ] + = f [ i ] [ j ] [ k ] ∗ j ∗ 2 f[i + 1][j][k + 1] += f[i][j][k] * j * 2 f[i+1][j][k+1]+=f[i][j][k]j2
\qquad <2> 给1个儿子,提供一个空位,给父亲: f [ i + 1 ] [ j − 1 ] [ k ] + = f [ i ] [ j ] [ k ] ∗ k ∗ ( j − 1 ) ∗ 2 f[i + 1][j-1][k] += f[i][j][k] * k * (j - 1) * 2 f[i+1][j1][k]+=f[i][j][k]k(j1)2
\qquad <3> 给2个儿子,不提供空位,不给父亲: f [ i + 1 ] [ j − 1 ] [ k − 1 ] + = f [ i ] [ j ] [ k ] ∗ j ∗ ( j − 1 ) f[i+1][j-1][k -1] += f[i][j][k] * j * (j - 1) f[i+1][j1][k1]+=f[i][j][k]j(j1)
\qquad <4> 给2个儿子,不提供空位,给父亲: f [ i + 1 ] [ j − 2 ] [ k − 1 ] + = f [ i ] [ j ] [ k ] ∗ k ∗ ( j − 1 ) ∗ ( j − 2 ) f[i+1][j-2][k-1] += f[i][j][k] * k * (j - 1) * (j - 2) f[i+1][j2][k1]+=f[i][j][k]k(j1)(j2)
细节很多,复杂度 O ( 6 ∗ n 3 ) O(6*n^3) O(6n3)

#include<bits/stdc++.h>
using namespace std;
#define LL long long 
#define MM %MOD
const int MAX = 310;
const int MOD = 1e9 + 7;
int tid, T, l[MAX], r[MAX], n;
LL f[MAX][MAX][2 * MAX]; //表示枚举i个节点, 有j颗树, 有k个空位可以填儿子 
void slove() {
	memset(f, 0, sizeof(f));
	f[1][1][0] = 1; //因为最后一个点有儿子一定不合法 
	if(l[1] != 0) {
		printf("0\n");
		return ;
	}
	for(int i = 1; i < n; i++) { //第几个节点 
		for(int j = 1; j <= i; j++) { //有j棵树 
			for(int k = 0; k <= 2 * i; k++) { 
				for(int s = l[i + 1]; s <= r[i + 1]; s++) {
					if(s == 0) { //对于儿子数目为0 
						f[i + 1][j + 1][k] = (f[i + 1][j + 1][k] + f[i][j][k]) MM; //考虑新开一个点
						if(k) f[i + 1][j][k - 1] = (f[i + 1][j][k - 1] + f[i][j][k] * k MM) MM; //考虑成为别人的儿子
					} 
					else if(s == 1) { 
						f[i + 1][j][k] = (f[i + 1][j][k] + (f[i][j][k] * j MM * 2LL MM)) MM; //考虑有1个儿子,不和别人连 
						if(k && j >= 1) f[i + 1][j - 1][k - 1] = (f[i + 1][j - 1][k - 1] + (f[i][j][k] * 2LL MM * k MM * (j - 1) MM)) MM; //考虑有一个儿子,连一个儿子,再和别人连 先连儿子在看父亲无法转,先连父亲,在看儿子 
					}
					if(s == 2) {
//						 ; //考虑没有儿子,到后面一定会不合法
						f[i + 1][j][k + 1] = (f[i + 1][j][k + 1] + (f[i][j][k] * j MM * 2LL MM) ) MM;  // 考虑一个儿子,不和父亲连,提供一个空位 
						if(j) f[i + 1][j - 1][k] = (f[i + 1][j - 1][k] + (f[i][j][k] * k MM * (j - 1) MM * 2LL MM) ) MM; //考虑一个儿子,和父亲连,提供一个空位 
						if(j >= 2) f[i + 1][j - 1][k] = (f[i + 1][j - 1][k] + (f[i][j][k] * (j) MM * (j - 1) MM) ) MM; //考虑两个儿子,不和父亲连,不提供空位 
						if(j >= 3 && k) f[i + 1][j - 2][k - 1] = (f[i + 1][j - 2][k - 1] + (f[i][j][k] * k MM * (j - 1) MM * (j - 2) MM) ) MM; //考虑两个儿子,和父亲连,不提供空位 
					}
				}
//				printf("f[%d][%d][%d] %d\n", i, j, k, f[i][j][k]);	
			}
		}
	}
	cout<<f[n][1][0] MM<<endl;
}
int main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d%d",&tid, &T);
	while(T, T--) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i++) scanf("%d%d", &l[n - i + 1], &r[n - i + 1]);
		slove(); 
	}
	
	return 0;
} 

书信:

在这里插入图片描述
在这里插入图片描述

迷宫 :

在这里插入图片描述
在这里插入图片描述

posted @ 2023-10-25 18:42  Nogtade  阅读(2)  评论(0编辑  收藏  举报  来源