GRYZ20211028模拟赛解题报告

写在前面

期望得分:\(100+100+30 \sim 60 = 230 \sim 260pts\)
实际得分:\(90 + 96 + 50 = 236pts\)

(第二题十二个测试点,每个测试点 \(8pts\)

今天轮到三区出题了,龙又搬了他那不知几年前搞的题。

也可能是感觉我们前两天考的太烂于是换了套简单的(((

T1

你发现出现次数 \(\ge \frac{n+1}{2}\) 的数不会太多,然后你直接离散化统计出现次数找到那几个出现次数 \(\ge \frac{n+1}{2}\) 的数,暴力判断需要翻转几次,对次数取 \(\min\) 即可。

为什么挂了 10pts ?因为他这个点卡快读 /fn/fn

/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 6e5+500;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node { int x, y; }a[MAXN];
int n, ans, res;
int date[MAXN << 1], Cnt = 0, date_num = 0;
int cnt[MAXN << 1];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int main()
{
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
	scanf("%d", &n); ans = INF;
    for(int i = 1; i <= n; ++i) {
	    scanf("%d%d", &a[i].x, &a[i].y);
	    date[++date_num] = a[i].x, date[++date_num] = a[i].y;
    }
    sort(date + 1, date + date_num + 1), date[0] = -INF;
    for(int i = 1; i <= date_num; ++i) if(date[i] != date[i - 1]) date[++Cnt] = date[i];
    for(int i = 1; i <= n; ++i) {
        a[i].x = lower_bound(date + 1, date + Cnt + 1, a[i].x) - date;
        a[i].y = lower_bound(date + 1, date + Cnt + 1, a[i].y) - date;
    }
    for(int i = 1; i <= n; ++i) {
        if(a[i].x == a[i].y) cnt[a[i].x] ++;
        else cnt[a[i].x] ++, cnt[a[i].y] ++;
    }
    for(int i = 1; i <= Cnt; ++i) {
        if(cnt[i] >= (n + 1) / 2) {
            int res = 0;
            for(int j = 1; j <= n; ++j) {
                if(a[j].x == i) {
                    res ++;
                }
            }
            ans = min(ans, max((n + 1) / 2 - res, 0));
        }
    }
    if(ans == INF) puts("Impossible");
    else printf("%d\n", ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T2

首先你要知道这个交换相邻元素使序列排好序是让你求逆序对。

然后你考虑确定出这几个子串的大小关系。

考虑自定义一个 cmp 直接排序,因为空间关系,我们要对起始位置排序而不是把每个子串列出来排序。

时间复杂度 \(\mathcal O(nm \log n)\)

考虑如何优化。

发现瓶颈在于比较两个串的大小关系,然后你考虑 Hash + ST 表,然后你就可以在 \(\log m\) 的时间内比较两个串的大小关系了。

时间复杂度 \(\mathcal O(n \log m \log n)\)

ST 表几乎没用过,但它并没有让我调太多时间,这很好。

/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
const int base = 19260817;

int n, m, ans = 0;
int a[MAXN], c[MAXN];
int Pow[MAXN], st[MAXN][18];
char s[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

namespace Bit {
    int sum[MAXN];
    int lb(int x) { return x & -x; }
    void Modify(int x, int k) { for(; x <= n; x += lb(x)) sum[x] += k; }
    int Query(int x) { int res = 0; for(; x; x -= lb(x)) res += sum[x]; return res; }
}

bool cmp(int x, int y) {
    int len = 0;
    for(int j = 16; j >= 0; --j) {
        if(x + (1 << j) > n || y + (1 << j) > n) continue;
        if(st[x + len][j] == st[y + len][j]) {
            len = len + (1 << j);
        }
    }
    if(len >= m) return false;
    for(int i = len + 1; i <= m; ++i) {
        if(s[x + i - 1] < s[y + i - 1]) return true;
        if(s[x + i - 1] > s[y + i - 1]) return false; 
    }
    return false;
}

bool Check(int l, int r) {
    int len = 0;
    for(int j = 16; j >= 0; --j) {
        if(l + (1 << j) > n || r + (1 << j) > n) continue;
        if(st[l + len][j] == st[r + len][j]) {
            len = len + (1 << j);
        }
    }
    if(len >= m) return true;
    else return false;
}

signed main()
{
    freopen("sort.in","r",stdin);
    freopen("sort.out","w",stdout);
	n = read(), m = read();
	cin >> s + 1;
	Pow[0] = 1;
    for(int i = 1; i <= n + m; ++i)  Pow[i] = Pow[i - 1] * base % mod;
	for(int i = 1; i <= m; ++i) s[n + i] = '1';
	s[n + m + 1] = '\0';
	for(int i = 1; i <= n; ++i) {
	    st[i][0] = s[i];
    }
    for(int i = 1; i <= 16; ++i) {
        for(int j = 1; j <= n; ++j) {
            st[j][i] = st[j][i - 1] * Pow[(1 << i - 1)] % mod + st[j + (1 << i - 1)][i - 1];
            st[j][i] %= mod;
//            cout<<i<<" "<<j<<' '<<st[j][i]<<"\n";
        }
    }
	for(int i = 1; i <= n; ++i) a[i] = i;
	sort(a + 1, a + n + 1, cmp);
	c[a[1]] = 1;
	for(int i = 2; i <= n; ++i) {
	    if(Check(a[i], a[i - 1])) c[a[i]] = c[a[i - 1]];
        else c[a[i]] = i;
    }
	for(int i = n; i >= 1; --i) {
	    int res = Bit::Query(c[i] - 1);
	    ans = ans + res;
	    Bit::Modify(c[i], 1);
    }
    printf("%lld\n", ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

T3

用 set 维护每一块巧克力然后爆搜切哪 \(k-1\) 刀。

时间复杂度 \(\mathcal O(\text{巨大})\)

反正大样例跑了两个半小时都没跑完。

实测只能过 20 pts ?还有两个点 WA 掉了。

然后你又想了另外一种方法,你先预处理出每个 \(a_i\) 能被那些矩形所拼。

然后你 dfs 每个 \(a_i\) 用哪个矩形,这样就做有 50pts 的好成绩。还是有两个点 WA 掉了,很奇怪(和上面 WA 掉的两个点不一样)

正解是记忆化搜索,设 \(f[sx][sy][ex][ey][S]\) 表示 \((sx,sy) - (ex, ey)\) 这块矩形能否组成的一部分 \(a_i\) 的组成的集合 \(S\)

20pts Code

/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node {
    int lx, ly, rx, ry;
    bool operator < (const node &b) const { 
        if(lx != b.lx) return lx < b.lx; 
        if(ly != b.ly) return ly < b.ly; 
        if(rx != b.rx) return rx < b.rx; 
        return ry < b.ry;
    }
};

int n, m, K;
int a[14][14], s[14][14], b[20];
int cnt[1010];
bool vis[1010], flag[1010], Flag = false;
multiset<node> S;

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int Calc(int lx, int ly, int rx, int ry) {
    return s[rx][ry] - s[lx - 1][ry] - s[rx][ly - 1] + s[lx - 1][ly - 1];
}

void dfs(int pos) {
    if(pos == K) {
//        cout<<pos<<"\n";
        int res = 0;
        for(multiset<node>::iterator it = S.begin(); it != S.end(); ++it) {
            int x = Calc(it->lx, it->ly, it->rx, it->ry);
            if(!flag[x] && vis[x]) flag[x] = true, res++;
        }
        for(multiset<node>::iterator it = S.begin(); it != S.end(); ++it) {
            int x = Calc(it->lx, it->ly, it->rx, it->ry);
            flag[x] = false;
        }
        if(res == K) Flag = true;
        return ;
    }
    multiset<node> E = S;
    for(multiset<node>::iterator it = E.begin(); it != E.end(); ++it) {
        node x = *it;
        S.erase(x);
        for(int i = x.lx; i < x.rx; ++i) {
            S.insert((node){x.lx, x.ly, i, x.ry});
            S.insert((node){i + 1, x.ly, x.rx, x.ry});
            dfs(pos + 1);
            S.erase((node){x.lx, x.ly, i, x.ry});
            S.erase((node){i + 1, x.ly, x.rx, x.ry});
            if(Flag) return ;
        }
        for(int i = x.ly; i < x.ry; ++i) {
            S.insert((node){x.lx, x.ly, x.rx, i});
            S.insert((node){x.lx, i + 1, x.rx, x.ry});
            dfs(pos + 1);
            S.erase((node){x.lx, x.ly, x.rx, i});
            S.erase((node){x.lx, i + 1, x.rx, x.ry});
            if(Flag) return ;
        }
        S.insert(x);
    }
}

int main()
{
    freopen("chocolate.in","r",stdin);
    freopen("chocolate.out","w",stdout);
	int T = read();
	while(T--) {
	    S.clear(); Flag = false;
	    bool fjh = false;
	    n = read(), m = read(), K = read();
	    for(int i = 1; i <= n; ++i) {
	        for(int j = 1; j <= m; ++j) {
	            a[i][j] = read();
	            if(a[i][j] != 1) fjh = true;
	            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
            }
        }
        memset(vis, false, sizeof vis);
        for(int i = 1; i <= K; ++i) b[i] = read(), vis[b[i]] = true;
        int sum = 0;
        for(int i = 1; i <= K; ++i) sum = sum + b[i];
        if(n == 1 && !fjh) {
            if(sum == m) puts("yes");
            else puts("no");
            continue;
        }
        if(sum != s[n][m]) {
            puts("no");
            continue;
        }
        S.insert((node){1, 1, n, m});
        cnt[s[n][m]]++;
        dfs(1);
        if(Flag) puts("yes");
        else puts("no");
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

50pts Code

/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct node {
    int sx, sy, ex, ey;
};

int n, m, K;
int a[20][20], s[20][20];
int b[22];
bool Flag, vis[20][20];
vector<node> V[20];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

int Calc(int lx, int ly, int rx, int ry) {
    return s[rx][ry] - s[lx - 1][ry] - s[rx][ly - 1] + s[lx - 1][ly - 1];
}

void dfs(int pos) {
    if(pos == K + 1) {
        Flag = true;
        return ;
    }
    for(int i = 0, M = V[pos].size(); i < M; ++i) {
        bool flag = false;
        int sx = V[pos][i].sx, ex = V[pos][i].ex, sy = V[pos][i].sy, ey = V[pos][i].ey;
        for(int j = sx; j <= ex; ++j) {
            for(int k = sy; k <= ey; ++k) {
                if(vis[j][k]) {
                    flag = true;
                    break;
                }
            }
            if(flag) break;
        }
        if(!flag) {
            for(int j = sx; j <= ex; ++j) {
                for(int k = sy; k <= ey; ++k) {
                    vis[j][k] = true;
                }
            }    
            dfs(pos + 1);
            if(Flag) return ;
            for(int j = sx; j <= ex; ++j) {
                for(int k = sy; k <= ey; ++k) {
                    vis[j][k] = false;
                }
            }            
        } 
    }
}

int main()
{
    freopen("chocolate.in","r",stdin);
    freopen("chocolate.out","w",stdout);
	int T = read();
    while(T--) {
        n = read(), m = read(), K = read();
        bool fjh = false;
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j<= m; ++j) {
                a[i][j] = read();
                if(a[i][j] != 1) fjh = true;
                s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]; 
            }
        }
        int sum = 0;
        for(int i = 1; i <= K; ++i) {
            b[i] = read();
            sum = sum + b[i];
            V[i].clear();
            for(int sx = 1; sx <= n; ++sx) {
                for(int sy = 1; sy <= m; ++sy) {
                    for(int ex = sx; ex <= n; ++ex) {
                        for(int ey = sy; ey <= m; ++ey) {
                            int x = Calc(sx, sy, ex, ey);
                            if(x == b[i]) V[i].push_back((node){sx, sy, ex, ey});
                        }
                    }
                }
            }
        }
        if(n == 1 && !fjh) {
            if(sum == s[n][m]) puts("yes");
            else puts("no");
            continue;
        }
        if(sum < s[n][m]) {
            puts("no");
            continue;
        }
        Flag = false;
        memset(vis, false, sizeof vis);
        dfs(1);
        if(!Flag) puts("no");
        else puts("yes");
    } 
    fclose(stdin);
    fclose(stdout);
    return 0;
}

其实我觉得两个部分分代码放着没什么必要,就是恶心恶心人(((

posted @ 2021-10-28 20:05  Suzt_ilymtics  阅读(69)  评论(1编辑  收藏  举报