2022NOIPA层联测3

以暴风骤雨的气势,也像熊熊燃烧的烈火,我将填平所有的深渊,也将照亮所有的夜晚,不到结束的时刻,就是一切未成定局。

 

问题 A: 【2022NOIP联测3 10月5日】A

大概是我头一次场上写对线段树。%%%Chen_jr的线段树模板,又美观又好背!

发现只有a[1]的排名对他的去留产生影响,所以数据可以用大于/小于 1/0 记录,每个人能力值互不相同就可以把这个数值当成基准线,记录每一年年初时基准线的排名,在变动之后基准线的排名不能超过lim(不能过于靠后),而且每次只会改变某一年中的某一个值,使基准排名增大的影响是不能被消掉的,因为要保证基准不被删除前面的就更能留下,题意就可以转化成对一个数组(由基准每年的排名构成),只有区间+1或-1操作,询问是否每个值都在界内。

然后继续转化,把边界和当前状态作差,每次对差值修改,询问是否存在一个数值小于0.最小值一定最有风险。So--线段树维护区间最小值,支持区间修改。

(因为区间修改只有后缀,我本来还想reverse一下然后用树状数组,忽然发现树状数组维护前缀和但是跟前缀修改似乎关系不大,而差分又只能单点查询……)

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n, m, q, rnk[maxn], lim[maxn], ls[maxn], B;
vector<bool> ve[maxn];

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

struct tree 
{
    struct node 
    {
        int min, lazy;
    }t[maxn<<2];
    void pushup(int x)
    {
        t[x].min = min(t[x<<1].min, t[x<<1|1].min);
    }
    void build(int x, int l, int r)
    {
        if(l == r)
        {
            t[x].min = ls[l]; return;
        }
        int mid = (l + r) >> 1;
        build(x<<1, l, mid);
        build(x<<1|1, mid+1, r);
        pushup(x);
    }
    void pushdown(int x)
    {
        int ls = x << 1, rs = x << 1 | 1;
        int lz = t[x].lazy; t[x].lazy = 0;
        t[ls].min += lz; t[ls].lazy += lz;
        t[rs].min += lz; t[rs].lazy += lz;
    }
    void update(int x, int l, int r, int L, int R, int val)
    {
        if(L <= l && r <= R)
        {
            t[x].min += val; t[x].lazy += val;
            return;
        }
        if(t[x].lazy) pushdown(x);
        int mid = (l + r) >> 1;
        if(L <= mid) update(x<<1, l, mid, L, R, val);
        if(R > mid) update(x<<1|1, mid+1, r, L, R, val);
        pushup(x);
    }
    int query(int x, int l, int r, int L, int R)
    {
        if(L <= l && r <= R) return t[x].min;
        int mid = (l + r) >> 1;
        if(t[x].lazy) pushdown(x);
        int ans = inf;
        if(L <= mid) ans = min(ans, query(x<<1, l, mid, L, R));
        if(R > mid) ans = min(ans, query(x<<1|1, mid+1, r, L, R));
        return ans;
    }
}t;

int main()
{
    n = read(); m = read(); q = read();
    B = read();
    for(int i=1; i<n; i++) 
    {
        int x = read(); if(x > B) rnk[1]++;
    }
    rnk[1]++;
    for(int i=1; i<=m; i++)
    {
        int b = read(); lim[i] = n-b;
        for(int j=1; j<=b; j++)
        {
            int x = read(); 
            if(x > B) ve[i].push_back(1), rnk[i+1]++;
            else ve[i].push_back(0);
        }
    }
    for(int i=2; i<=m; i++) rnk[i] += rnk[i-1];
    for(int i=1; i<=m; i++)
    {
        ls[i] = lim[i] - rnk[i];
    }
    t.build(1, 1, m);
    while(q--)
    {
        int year = read(), num = read(), val = read();
        num--;
        if(!ve[year][num])
        {
            if(val < B)
            {
                if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
                else printf("1\n");
            }
            else 
            {
                ve[year][num] = 1;
                if(year != m) t.update(1, 1, m, year+1, m, -1);
                if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
                else printf("1\n");
            }
        }
        else 
        {
            if(val > B)
            {
                if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
                else printf("1\n");
            }
            else 
            {
                ve[year][num] = 0;
                if(year != m) t.update(1, 1, m, year+1, m, 1);
                if(t.query(1, 1, m, 1, m) < 0) printf("0\n");
                else printf("1\n");
            }
        }
    }
    
    return 0;
}

 

问题 B: 【2022NOIP联测3 10月5日】B

感觉和预设dp有一点相似之处,打算复习一下,翻到了一些优质博客推荐一下大概没关系?

%%%

本来正为找不到我的dp的问题而不知所措,直到鹤到了gtm1514的题解,才发现我写了个什么鬼,填数没2倍,3个空格的情况还忘了讨论。调不出来只好交了个dfs:

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n, mod, ans;
bool v[35];

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

//int a[35], siz;

void dfs(int num)
{
    //printf("dfs(%d)\n", num);
    //for(int i=1; i<=n; i++) printf("%d ", v[i]);
    //printf("\n");
    if(num > n) 
    {
        //for(int i=1; i<=siz; i++) printf("%d ", a[i]);
        //printf("\n");
        ans++; if(ans >= mod) ans -= mod;
        return;
    }
    for(int i=1; i<=n; i++)
    {
        if(v[i]) continue;
        //a[++siz] = i;
        v[i] = 1; int x = num+1; bool f1 = 0, f2 = 0;
        if(i-2>=1 && !v[i-1] && v[i-2]) x++, v[i-1] = 1, f1 = 1;
        if(i+2<=n && !v[i+1] && v[i+2]) x++, v[i+1] = 1, f2 = 1;
        dfs(x);
        //a[siz--] = 0;
        v[i] = 0;
        if(f1) v[i-1] = 0;
        if(f2) v[i+1] = 0;
    }
}

int main()
{
    n = read(); mod = read();
    dfs(1);
    printf("%d\n", ans);
    
    return 0;
}

设dp[i][j]为当前有i个电脑开启,形成了j个连续段的方案数,本来想着连续段当然不会比开启的电脑还多所以第二层循环到i,但其实需要循环到n??洛古题解的版本似乎到i就够了……然后还是鹤:转移的时候有新建段,段扩增,段合并3种。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 402;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n, mod;
ll f[maxn][maxn];

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

inline void add(ll &x, ll y) {x = (x+y)%mod;}

int main()
{
    n = read(); mod = read();
    f[0][0] = 1;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(f[i-1][j-1]) add(f[i][j], j*f[i-1][j-1]%mod);//新建段
            if(f[i-1][j]) add(f[i][j], 2*j*f[i-1][j]%mod);//段扩增,向左or向右
            if(i>1 && f[i-2][j]) add(f[i][j], 2*j*f[i-2][j]%mod);//中间有1个空位
            if(i>1 && f[i-2][j+1]) add(f[i][j], 2*j*f[i-2][j+1]%mod);//中间有两个空位
            if(i>2 && f[i-3][j+1]) add(f[i][j], j*f[i-3][j+1]%mod);//中间有3个空位
        }
    }
    printf("%lld\n", f[n][1]);
    
    return 0;
}

 

 

问题 C: 【2022NOIP联测3 10月5日】C

暴力居然A了?!就枚举k然后把它拼出来判断字典序……

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n;
string s, t;

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

void work(int k)
{
    string fc;
    int fl = 1;
    for(int i=0; i<(1<<n); i++)
    {
        fc += s[i^k];
        if(fl && s[i^k] > t[i]) return;
        if(s[i^k] < t[i]) fl = 0;
    }
    t = fc;
}

int main()
{
    n = read();
    cin >> s;
    t = s;
    for(int i=1; i<(1<<n); i++)
    {
        work(i);
    }
    cout << t << endl;
    
    return 0;
}

 

问题 D: 【2022NOIP联测3 10月5日】D

只鹤懂了官方题解上的特判测试点2……

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 10;
const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n;

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

int main()
{
    n = read();
    printf("1 %d\n", n); printf("1 %d\n", n-1);
    for(int i=2; i<=n-2; i++)
    {
        printf("%d %d\n", i, n);
    }
    exit(0);
    
    return 0;
}

场上我只想到了dfs枚举每个点的fa,然后建树,挨个判断区间是否合法,Sily极了……

终于鹤到了题解from Chen_jr

构造方式和题解上的有微小的差异,1连n连2,剩下的全都连1。假设所有的好区间只有包含关系,单点也算好区间,就可以把这些互不连通的区间看成互不连通的点,用特殊构造来解决。

如果出现交叉关系,由于区间的交和并必然是好区间,交叉区间的处理独立不干扰。如果某个区间在处理时发现存在比它小的区间的并包含了它,说明已经满足要求不用处理了。

upd:找到的最后一个集合需要用最大元素做代表元。直接赋值的话最大元素左端点已经扔出去了。然后既然已经连边了,merge操作也要配套。

大概就是鹤吧……

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2005;
//const ll mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

vector<int> v;
bool mp[maxn][maxn];
int n, fa[maxn], mi[maxn], mx[maxn];
char c[maxn];

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

int find(int x)
{
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
    x = find(x); y = find(y);
    if(x != y)
    {
        fa[y] = x;
        mi[x] = min(mi[x], mi[y]);
        mx[x] = max(mx[x], mx[y]);
    }
}

void solve(int l, int r)
{
    for(int i=l; i<=r; i=mx[find(i)]+1) v.push_back(i);
    v.back() = r;
    int ls = v.back(), s = v.size();
    for(int i=0; i<s-1; i++) merge(v[i], ls);
    if(v.size() > 1)
    {
        printf("%d %d\n", v[0], v.back());
        printf("%d %d\n", v[1], v.back());
        v.pop_back();
        for(int i=2; i<v.size(); i++)
        {
            printf("%d %d\n", v[i], v[0]);
        }
    }
    v.clear();
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++)
    {
        scanf("%s", c);
        for(int j=i; j<=n; j++) mp[i][j] = (c[j-i] == '1');
    }
    for(int i=1; i<=n; i++) fa[i] = mx[i] = mi[i] = i;
    for(int i=2; i<=n; i++)
    {
        for(int j=1; i+j-1<=n; j++)
        {
            int l = j, r = j + i - 1;
            if(mp[l][r] == 0) continue;
            if(find(l) != find(r))
            {
                if(mx[find(l)] + 1 == mi[find(r)])
                {
                    printf("%d %d\n", l, r);
                    merge(l, r);
                }
                else solve(l, r);
            }
        }
    }
    
    return 0;
}
posted @ 2022-10-05 14:14  Catherine_leah  阅读(15)  评论(0编辑  收藏  举报
/* */