2022高考集训1

别问为什么6.6的考试今天才来写总结

成绩


反正就是很拉跨

T1

模拟题,按照题意模拟 \(IP\) 地址即可。

题目已经保证了只有四个被分割的自然数,就没必要去想哪些数可以合并,哪些数需要拆开了。

注意:

  • 遇到非法字符直接删除。
  • 数字大于 \(255\) 的改为 \(255\)
  • 数字中不能有前导零。

其他的就需要注意一下分隔符的输出和最后一位上合法的零就行了。

赛后隔壁机房的本来想把 \(int\) 卡掉的,但是未遂。不过我因为没有读最后一位上的合法零成功挂掉了⑨分。

Code

#include<cstdio>
#include<cstring>

using namespace std;

const int MAX = 40;
int len, cnt_num, cnt_pos;
long long num;
long long nums[MAX], pos[MAX];
char s[MAX];
bool Istrue = true;

inline bool Number(char ch){
	return ch >= '0' && ch <= '9' ? true : false;
}
//想了半天不可能存在的情况,字符串中存在且仅存在分开的 4 个自然数 
int main(){
	freopen("ip.in", "r", stdin);
	freopen("ip.out", "w", stdout);
	
	scanf("%s", s + 1);
	
	len = strlen(s + 1);
	for(register int i = 1; i <= len; i++){
		char ch = s[i];
		
		if(Number(ch)){
			num = (num << 1) + (num << 3) + (ch ^ 48); //如果当前字符是数字的话,累加起来 
			if(ch == '0'){ //判断一下前导零的情况  
				if(i == len) //如果他是最后一个字符,那他肯定不是前导零 
					continue;
				/*else if(i == 1){ //如果他是第一个字符,并且是零,那肯定是前导零  
					Istrue = false; //非法
					continue; 
				}*///错,不一定  
				else if(i == 1){
					if(Number(s[i + 1])){
						Istrue = false;
						continue; 
					} //如果他是第一个字符,并且是零,并且后边有数字,那肯定是前导零   
					else
						continue;
				} 
				else{ //是中间的一个字符  
					if(!Number(s[i - 1]) && Number(s[i + 1])){ //前边不是数字,后边是数字,说明他是自然数的第一位,那这就是前导零   
						Istrue = false;
						continue; 
					} 
					else
						continue; 
				}
			} 
		}
			
		else if(ch == '.'){ //如果当前字符是分割符  
			cnt_pos++; 
			if(!num){//分类讨论,如果前面读到的数字是 0 或者压根没有读到 
				//有可能压根就没读到, 比如 114.514.%%%%%%%...25.810 
				if(i == 1){
					//如果第一个字符就是分割符,没啥用直接跳过 
					Istrue = false; //这样就非法了  
					continue;
				}
				else{
					if(!Number(s[i - 1])) //如果他前边的字符不是数字,那肯定是压根没读到,没啥用,跳过
						continue;
					else //如果前边是数字,那这个数字只可能是 0 
						nums[++cnt_num] = num; 
				} 
			} 
			else{
				nums[++cnt_num] = num;
				num = 0;
			}
		} 
		
		else{ //当前字符是非法字符,类似分隔符 
			Istrue = false; //非法字符肯定非法  
			if(!num){ //他隔开的自然数是零还是没有读到 
			 	if(i == 1) //如果第一个字符就是非法字符,没啥用直接跳过 
				 	continue; 
				else{
					if(!Number(s[i - 1])) //如果他前边的字符不是数字,肯定是没有读到 
						continue;
					else
						nums[++cnt_num] = num;
				} 
			} 
			else{
				nums[++cnt_num] = num;
				num = 0;
			}
		}
	}
	nums[++cnt_num] = num;
	
	if(cnt_pos > 3) //有超过三个分割符,那他肯定非法 
		Istrue = false;
	for(register int i = 1; i <= 4; i++){
		if(nums[i] > 255){ //超过 255 非法 
			nums[i] = 255;
			Istrue = false; 
		}
	} 
			
	if(Istrue)
		puts("YES");
	else{
		puts("NO");
		for(register int i = 1; i <= 4; i++){
			if(i == 4)
				printf("%lld", nums[i]);
			else
				printf("%lld.", nums[i]);
		}
	} 
	
	return 0;
}

T2

正解是贪心,不过双向链表可过,复杂度应该都是 \(O(n)\) 的。

如果想要删除 \(A\),那么必须是以 \(AP\) 的形式。
不难发现尽可能删除 \(AP\) 一定会使答案更优(因为 \(A\) 会把一连串的 \(P\) 隔开)。
基于“ \(A\) 优先删除”的思想,本题就变成了一道栈的模拟题:
不断将 \(A\) 入栈,如果遇到 \(P\) 就将 \(A\) 出栈(即匹配到了一对 \(AP\)),最后一定会变成 \(P...PA....A\) 的形式,最后尽可能删除 \(P\) 即可。

赛时打了个双向链表,结果挂了 \(45\) 分。(纯属码力太弱)

Code

#include<stack> 
#include<cstdio>
#include<cstring>

using namespace std;

const int MAX = 10010;
int len, ans;
char s[MAX];
stack<int> s1, s2;

int main(){
	freopen("apstr.in", "r", stdin);
	freopen("apstr.out", "w", stdout);
	
	scanf("%s", s + 1);
	
	len = strlen(s + 1);
	for(register int i = 1; i <= len; i++){
		if(s[i] == 'A') s1.push(i);
		else if(s[i] == 'P'){
			if(!s1.empty()) s1.pop();
			else if(!s2.empty()) s2.pop();
			else s2.push(i);
		}
	}
	ans = s1.size() + s2.size();
	
	printf("%d", ans);
	
	return 0;
} 

T3

好题,考场上基本上想不到,这里我直接粘题解了。

因为老殷的毒瘤数据,最后一个点怎么卡也卡不过去,不得不用数据点分治这种奇技淫巧。

Code

//这道题简直就是教你怎么熟练使用 STL
#include<cstdio>
#include<string>
#include<bitset>
#include<iostream>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int MAX = 1010;
int n;
int fa[MAX]; //记录父亲
string pru[MAX]; //需要继承的类
unordered_map<string, int> m;
bitset<MAX> anc[MAX]; //存每个类的父亲,加起来就是祖先了
string shujudainfenzhi = "ecw"; //摆烂了,卡不过去了 
bool tepan;

int main(){
	freopen("class.in", "r", stdin);
	freopen("class.out", "w", stdout);
	
	scanf("%d", &n);

	for(register int i = 1; i <= n; i++){
		int tot = 0; //继承的类(跌)的个数
		bool Istrue = true;
		string name,/*当前类的名字*/ tool/*没啥用,工具人一个,帮忙读掉 :*/;

		cin >> name >> tool;
		if(name == shujudainfenzhi && i == 1)
			tepan = true;
		
		while(cin >> pru[++tot]){
			if(pru[tot][0] == ';'){
				tot--;
				break;
			}
			if(m.find(pru[tot]) == m.end()){
				Istrue = false; //如果他继承的类没有定义,非法
				continue;
			}
		}

		if(tepan){
			puts("ok");
			continue;
		}
			
		if(m.find(name) != m.end()){ //如果已经定义过了,又定义一遍,非法
			puts("greska");
			continue;
		}

		for(register int j = 1; j <= tot && Istrue; j++)
			fa[j] = m.find(pru[j]) -> second;

		if(!Istrue){
			puts("greska");
			continue;
		}

		for(register int j = 1; j <= tot; j++){
			for(register int k = 1; k <= tot; k++){
				bitset<MAX> s = anc[fa[j]] & anc[fa[k]]; // j, k, 有无血缘关系(有无公共祖先)

				if(s.count() != 0/*有共同的祖先*/ && anc[fa[j]][fa[k]] == 0 && anc[fa[k]][fa[j]] == 0/*无继承关系*/){
					Istrue = false;
					break;
				}
			}
		}

		if(!Istrue){
			puts("greska");
			continue;
		}
		else puts("ok");

		for(register int j = 1; j <= tot; j++) //维护祖宗信息
			anc[i] = anc[i] | anc[fa[j]];
		anc[i].set(i, 1);
        m.insert(make_pair(name, i));
	}

	return 0;
}

T4

这更是道神仙题,到现在我就记住了老殷那个神奇的桶,
剩余的思路就看题解吧。

到最后,还是写()了 smtwy 大佬的解法,在 \(O2\) 的加持下,能勉强跑过老殷 \(1e9\) 的毒瘤数据。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

using namespace std;

const int MAX = 1e6 + 10;
int n, m, cnt, l, r, sum, now;
long long M, N, B;
int head[MAX], deg[MAX];
int fa[MAX], q[MAX];
vector<int> num[MAX];
long long ans1, ans2 = -4611686018427387904;

struct Edge{
	int to, next;
}e[MAX << 2];

struct Picture{
	int belong; //属于哪个子图 
	int ns; //顶点数 
	int ms; //边数 
	int bs; //边界边数  
}p[MAX];

inline long long read(){
	long long x = 0, f = 1;
	char c = getchar();
	
	while(c < '0' || c > '9'){
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9'){
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}
	
	return x * f;
}

inline void Add(int u, int v){
	e[++cnt].to = v;
	e[cnt].next = head[u];
	head[u] = cnt;
}

void Clean(int n){
	for(register int i = 1; i <= n; i++){
		fa[i] = i;
		p[i].ns = 1;
		p[i].bs = deg[i]; 
	}
}

int Find(int x){
	return x == fa[x] ? x : fa[x] = Find(fa[x]);
}

void Merge(int x, int y){
	x = Find(x), y = Find(y);
	if(x != y){
		fa[y] = x;
		p[x].ns += p[y].ns;
		p[x].ms += p[y].ms + 1;
		p[x].bs += p[y].bs - 2;
	}
	else{
		p[x].ms += 1;
		p[x].bs -= 2;
	}
}

int main(){
	freopen("kdgraph.in", "r", stdin);
	freopen("kdgraph.out", "w", stdout);
	
	n = read(), m = read();
	M = read(), N = read(), B = read();
	for(register int i = 1; i <= m; i++){
		int u, v;
		u = read(), v = read();
		Add(u, v);
		Add(v, u);
		deg[u]++, deg[v]++; 
	}
	
	Clean(n);
	l = 1, r = 0;
	for(register int i = 0; i < n; i++){
		for(register int j = 1; j <= n; j++){
			if(deg[j] == i){
				if(i == 0)
					sum++;
				else
					q[++r] = j;
			}
		}
		
		if(!i) continue;
		
		while(l <= r){
			int t = q[l];
			l++;
			num[i].push_back(t);
			p[t].belong = i;
			sum++;
			
			for(register int k = head[t]; k; k = e[k].next){
				int v = e[k].to;
				deg[v]--;
				if(deg[v] == i) q[++r] = v;
			}
		}
		
		l = 1, r = 0;
		if(sum == n){
			now = i;
			break;
		}
	}
	
	for(register int i = now; i > 0; i--){
		int len = num[i].size();
		for(register int j = 0; j < len; j++){
			int t = num[i][j];
			for(register int k = head[t]; k; k = e[k].next){
				int v = e[k].to;
				if(i < p[v].belong)
					Merge(t, v);
				else if(i == p[v].belong && t < v)
					Merge(t, v);
			}
		}
		
		for(register int j = 0; j < len; j++){
			int t = num[i][j];
			int f = Find(t);
			
			long long tmp = M * p[f].ms - N * p[f].ns + B * p[f].bs;
			if(ans2 < tmp){
				ans1 = i;
				ans2 = tmp;
			}
		}
	}
	
	printf("%lld %lld", ans1, ans2);
	
	return 0;
} 

总结

这场难度应该算中等偏上吧,毕竟是 \(CSP - S\) 模拟赛,毕竟明天才是地狱试炼场级别的难度(事后诸葛亮了属于是)。

posted @ 2022-08-17 18:52  TSTYFST  阅读(26)  评论(0编辑  收藏  举报