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\) 模拟赛,毕竟明天才是地狱试炼场级别的难度(事后诸葛亮了属于是)。
以下为博客签名,与博文无关。
只要你们不停下来,那前面就一定有我。所以啊,不要停下来~
本文来自博客园,作者:TSTYFST,转载请注明原文链接:https://www.cnblogs.com/TSTYFST/p/16596431.html