2022高考集训1


这次真的考炸了······

T1 2A

分析

数组开小了aaaaaa......................(╯‵□′)╯︵┻━┻

一百分就这么没了。。。

主要就是判断是否合法

唯一合法的时候,就是按照规则重新组织 IP 后,输入的字符串 = 输出的字符串

有几个错误情况:

1.字符串中除了“.”和数字,有其他字符,貌似没有空格,不确定可以用cin.getline输入

2.前导零

3.第四个数字后面有“.”,第一个数字前有“.”

4.中间有好几个“.”连在一起

5.数字大于255(一定要开long long储存数字,防止爆int,一个数字最多26位,就算爆long long,也是负数)

AC 代码
#include 
#include 
using namespace std;
#define ll long long
char s[40];
bool vis;
ll ans[5];
int main(){
    freopen("ip.in","r",stdin);
    freopen("ip.out","w",stdout);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin.getline(s,40);
    int l = strlen(s);
    for(int i = 0;i < l;++i){//有无其他字符
        if(!vis && s[i] != '.' && (s[i] < '0' || s[i] > '9')){
            printf("NO\n");
            vis = 1;
            break;
        }
    }
    //判断数字是否合法并记录数字
    int t = 0;
    while((s[t] < '0' || s[t] > '9') && t < l) t++;
    if(!vis && t){
        printf("NO\n");
        vis = 1;
    }
    for(int i = 1;i <= 4;++i){
        if(!vis && s[t] == '0'){
            printf("NO\n");
            vis = 1;
        }
        while(s[t] >= '0' && s[t] <= '9' && t < l){
            ans[i] = ans[i]*10 + (s[t] ^ 48);
            t++;
        }
        if(!vis && ans[i] > 255){
            printf("NO\n");
            vis = 1;
        }
        if(!vis && i == 4 && t != l){
            printf("NO\n");
            vis = 1;
        }
        while((s[t] < '0' || s[t] > '9') && t < l) t++;
    }
    if(!vis) printf("YES\n");
    else{
        for(int i = 1;i <= 4;++i){
            if(ans[i] < 0 || ans[i] <= 255) printf("%lld",ans[i]);
            else printf("255");
            if(i != 4) printf(".");
        }
    }
    return 0;
}

T2 2B

分析

这道题也比较简单,没错,是“比较简单”,T1纯属意外

因为PP和AP可以删除,AA不能删除

也就是说当目前字符串中,P的个数越多,越容易删除

所以优先删除AP更优

AC 代码
#include 
#include 
using namespace std;
#define ll long long
inline int in(){
    int x = 0;
    bool f = 1;
    char c = getchar();
    while(c > '9' || c < '0'){
        if(c == '-') f = 0;
        c = getchar();
    }
    while(c <= '9' && c >= '0'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    if(f) return x;
    else return -x;
}
//先合并AP更优?
//emmm.....大概吧
char s[10010];
int f[10010];
int ans;
int main(){
    freopen("apstr.in","r",stdin);
    freopen("apstr.out","w",stdout);
    scanf("%s",s);
    int l = strlen(s);
    f[0] = -1;
    for(int i = 1;i <= l;++i) f[i] = i-1;
    for(int i = 0;i < l;++i){
        if(s[i] == 'A' && s[i+1] == 'P'){
            f[i+2] = f[i];
            i++;
            continue;
        }
        if(f[i] >= 0  && s[i] == 'P'){
            f[i+1] = f[f[i]];
            continue;
        }
        if(s[i] == 'P' && s[i+1] == 'P'){
            f[i+2] = f[i];
            i++;
            continue;
        }
    }
    int t = l;
    while(t >= 0){
        ans++;
        t = f[t];
    }
    printf("%d",ans-1);
    return 0;
}

T3 2C

分析

首先肯定是判断声明是否正确,再连接边

条件一:如果重复声明,则为错误声明

条件二:如果要连边的点未声明,则为错误声明

条件三:如果会连成菱形,则为错误声明

对于前两个条件,用map就能解决

对于第三个条件菱形

首先要明确什么是菱形:

对于A,B,C,D四个类

B和C有公共祖先A

而且B不是C的祖先,C也不是B的祖先

B和C都是D的祖先

由于直接处理的时间复杂度很高,Lyin大佬造的数据过不去,只是题库的数据太水了(啊不是)

所以我们将与B有公共祖先,且与B无祖孙关系的C点存一下

如果D同时连接B和C,直接break

用bitset直接做&运算会方便许多

AC 代码

#include 
#include 
#include 
#include 
#include 
using namespace std;
#define ll long long
inline int in(){
    int x = 0;
    bool f = 1;
    char c = getchar();
    while(c > '9' || c < '0'){
        if(c == '-') f = 0;
        c = getchar();
    }
    while(c <= '9' && c >= '0'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    if(f) return x;
    else return -x;
}

//先判断声明是否正确,再连接边!!!
//条件一:如果重复声明,则为错误声明
//条件二:如果要连边的点未声明,则为错误声明
//条件三:如果会连成菱形,则为错误声明

//菱形:a -> b,a -> c
//b -> d c -> d
//一个点可能由许多个入度,也可能有许多个出度
//那么每次判定菱形就要去枚举每一个指向它的点
//emmm....
//也就是只要有相同的父节点就不行
//所以需要记录每个的根节点

//那么判定菱形的条件就是:
//设fa[]为每个结点的根节点
//当前边为 b -> d,c -> d
//b != fa[b] && c != fa[c] && fa[b] == fa[c]

const int N = 1010;
int n;
int fa[N],cnt;//记录要连边的父节点
int tot;
map mp;
bitset zx[N];//记录祖先
bitset b[N];//记录两个节点连接会炸
//A -> B,A -> C
//b中记录B,C
int main(){
    freopen("class.in","r",stdin);
    freopen("class.out","w",stdout);
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    n = in();
    while(n--){
        bool bj = 0;
        cnt = 0;
        string s,f1,f2;
        cin >> s;
        cin >> f1;
        cin >> f2;
        while(f2[0] != ';'){
            if(!mp[f2]) bj = 1;
            fa[++cnt] = mp[f2];
            cin >> f2;
        }
        if(mp[s]){
            printf("greska\n");
            continue;
        }
        bitsetjl;//记录父节点的祖先
        for(int i = 1;i <= cnt;++i) jl[fa[i]] = 1;
        for(int i = 1;i <= cnt;++i){
            if((jl&b[fa[i]]).any()){
                bj = 1;
                break;
            }
        }
        if(bj){
            printf("greska\n");
            continue;
        }
        printf("ok\n");
        mp[s] = ++tot;
        zx[tot][tot] = 1;
        for(int i = 1;i <= cnt;++i) zx[tot] |= zx[fa[i]];
        for(int i = 1;i < tot;++i){
            if(!zx[tot][i] && (zx[i]&zx[tot]).any())
                b[tot][i] = 1;//如果接下来有节点会连接tot和i,那么就会构成菱形
        }
    }
    return 0;
}

T4 2D

分析

赛时eafoo大佬暴力过了,赛后经过lyin大佬的极强数据,卡掉了几种算法

Lyin大佬的O(n+2m+nα)算法

#include 
#define fre(x) freopen( #x ".in", "r", stdin), freopen( #x ".out", "w", stdout )
using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef long double ldb;
const int N = 1e6 + 10;

mt19937 mt( (ull)(new char) );
int Rand ( int l, int r ) { return uniform_int_distribution<>(l,r)(mt); }

int n, m, kn, km, kb, du[N]; vector  vec[N];

struct BCJ
{
	int fa[N], sm[N], sn[N], sd[N];

	void Init ( int n ) { for( int i = 1; i <= n; ++i) fa[i] = i, sm[i] = 0, sn[i] = 1, sd[i] = du[i]; }

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

	void Merge ( int x, int y )
	{
		int fx = Find(x), fy = Find(y); if( fx == fy ) return; if( sn[fx] > sn[fy] ) swap( fx, fy );
		fa[fx] = fy, sm[fy] += sm[fx], sn[fy] += sn[fx], sd[fy] += sd[fx];
	}

	ll Calc ( int x ) { return sn[x] == 1 ? -LLONG_MAX : 0LL + 1LL * km * sm[x] + 1LL * kn * sn[x] + 1LL * kb * ( sd[x]-2LL*sm[x] ); }
}S;

struct Graph
{
	struct Edge { int to, next; } E[N<<1];
	int ind, head[N];

	void Insert ( int u, int v ) { ++ind, ++du[u], E[ind].to = v, E[ind].next = head[u], head[u] = ind; }

	void Link ( int u, int v ) { Insert( u, v ), Insert( v, u ); }

	vector  bin[N]; bool vis[N]; int top;

	bool Select ( int k, int &u ) { while( bin[top].empty() && top <= k ) ++top; if( top > k ) return false; u = bin[top][ bin[top].size()-1 ], bin[top].pop_back(); return true; }

	void Solve ()
	{
		for( int i = 1; i <= n; ++i) bin[ du[i] ].push_back( i ); top = 0;
		for( int k = 1, u = 0; k <= n; ++k) while( Select( k, u ) )
		{
		 	if( vis[u] ) continue; vec[k].push_back(u), vis[u] = true, S.sm[u] = top;
			for( int i = head[u]; i; i = E[i].next ) if( !vis[ E[i].to ] ) bin[ --du[ E[i].to ] ].push_back( E[i].to ), top = min( top, du[ E[i].to ] );
		}
		for( int i = 1; i <= n; ++i) vis[i] = 0;
		ll maxi = -LLONG_MAX, maxk = 0;
		for( int k = n; k >= 1; --k)
		{
			reverse( vec[k].begin(), vec[k].end() );
			for( auto u : vec[k] ) { vis[u] = true; for( int i = head[u]; i; i = E[i].next ) if( vis[ E[i].to ] ) S.Merge( u, E[i].to ); }
			for( auto u : vec[k] ) if( maxi < S.Calc( S.Find(u) ) ) maxi = S.Calc( S.Find(u) ), maxk = k;
		}
		cout << maxk << " " << maxi << "\n";
	}
}G;

void Solve ()
{
	cin >> n >> m >> km >> kn >> kb, kn = -kn;
	for( int i = 1; i <= m; ++i) { int a, b; cin >> a >> b, G.Link( a, b ); }
	S.Init(n), G.Solve();
}

signed main ()
{
	fre(kdgraph);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); Solve(); return 0;
}

经过smtwy的不懈努力,终于调出了自己的代码,据说是与题解思路一样

赛时多打了个“=”,挂了50分

以下是smtwy大佬的思路

首先,先建图,建议用前向星(Lyin大佬的数据太强啦,vector太慢,会挂掉)(下面的建议原因相同,就不再一一解释了)

同时记录一下每个点的度

然后我们去枚举第k-degree图

将度为k的点存到一起

注意:会有孤立点,所以循环要从0开始,把孤立点删掉

然后从最大的k,倒序将原图恢复

因为k+1-degree是k-degree的子图

将度为k的点与度大于k的点合并

合并时要注意边界边的条数,因为度为k的点与其他连通图连接的位置是两个图的边界边

所以要-2

smtwy大佬的题解:

AC 代码

#include 
#include 
#include 
using namespace std;
#define Re register int
#define ll long long
inline int in(){
    int x = 0;
    bool f = 1;
    char c = getchar();
    while(c > '9' || c < '0'){
        if(c == '-') f = 0;
        c = getchar();
    }
    while(c <= '9' && c >= '0'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    if(f) return x;
    else return -x;
}
const int N = 1e6+10;
struct node{
    int bl;//属于第几degree
    int sn,sm,sb;//顶点数,边数,边界边数
}s[N];
int n,m;
int a,b,c;
int mx = -1,du;
int head[N],nxt[N << 1],to[N << 1],cnt;
int d[N];
int fa[N];
int q[N],l,r;
ll res,ans;
vector num[N];
inline void add(int u,int v){
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
}
inline int find(int x){
    if(fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}
inline void merge(int u,int v){
    int x = find(u);
    int y = find(v);
    if(x != y){//不属于同一k_degree
        fa[y] = x;
        s[x].sn += s[y].sn;
        s[x].sm += s[y].sm + 1;
        s[x].sb += s[y].sb - 2;//连接两个不同的k_degree,边界边会变成连通边,那么边界边会减少2
    }else{
        s[x].sb -= 2;
        s[x].sm++;
    }
}
int main(){
    freopen("kdgraph.in","r",stdin);
    freopen("kdgraph.out","w",stdout);
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    n = in();
    m = in();
    a = in();
    b = in();
    c = in();
    ans = -1e17;
    int x,y;
    for(Re i = 1;i <= m;++i){
        x = in();
        y = in();
        d[x]++;
        d[y]++;
        add(x,y);
        add(y,x);
    }
    for(Re i = 1;i <= n;++i){//初始化
        fa[i] = i;
        s[i].sn = 1;
        s[i].sb = d[i];
        mx = max(mx,d[i]);
    }
    int tot = 0,mxx;
    for(Re i = 0;i <= mx;++i){//枚举k-degree子图,将每个点分到不同子图中
        l = 1;r = 0;
        for(Re j = 1;j <= n;++j)
            if(d[j] == i){
                if(i == 0) tot++;
                else q[++r] = j;
            }
        if(i == 0) continue;
        while(l <= r){
            int u = q[l];
            l++;
            tot++;
            num[i].push_back(u);
            s[u].bl = i;
            for(int k = head[u];k;k = nxt[k]){
                int v = to[k];
                d[v]--;
                if(d[v] == i) q[++r] = v;
            }
        }
        if(tot == n){
            mxx = i;
            break;
        }
    }
    //k+1_degree 属于 k_degree,所以倒序枚举,将图恢复
    for(int i = mxx;i > 0;--i){
        int sz = num[i].size();
        for(int j = 0;j < sz;++j){
            int u = num[i][j];
            for(int k = head[u];k;k = nxt[k]){
                int v = to[k];
                if(i < s[v].bl) merge(u,v);
                else if(i == s[v].bl && u < v) merge(u,v); 
            }
        }
        for(int j = 0;j < sz;++j){
            int k = find(num[i][j]);
            res = (ll)s[k].sm*a - (ll)s[k].sn*b + (ll)s[k].sb*c;
            if(ans < res){//不用取等,因为i是倒序枚举的
                du = i;
                ans = res;
            }
        }
    }
    printf("%d %lld\n",du,ans);
    return 0;
}

希望明天不会挂(? ?_?)?