2022NPIOA层联测13

数塔:

相等上传非常显然,重点是怎么二分(对于这种不知道更大的更优还是更小的更优的题,不知道选哪个二分模板。。)
大于等于和小于等于都可以,重要的是取等,就是保证答案在二分的区间内,
二分剩下的数是什么,剩下带等号的一方肯定合法,如果是大于等于,区间就向大的方向缩小
小于等于就向小的方向缩小
比它小的设为0,如果最上面的是0,说明答案小于它,否则答案大于等于它
用哪种形式二分的判断条件:找到答案的方向,找最大值=可行区间在左边,找最小值=可行区间在右边
格式配对的判断条件:边界是l和r相差1,目的是尽量尝试更优的情况
所以,找最大值,mid偏大+1再右移,找最小值,直接右移
实在不行那就试一下,看哪一版能过样例

code大于等于设为1
// ubsan: undefined
// accoders
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 5;

int T, l, r, n, a[maxn], b[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;
}

bool check(int mid)
{
    for(int i=1; i<=2*n-1; i++)
    {
        b[i] = (a[i] >= mid);
    }
    for(int i=0; i<=n-1; i++)
    {
        if(b[n+i] == b[n+i+1]) return b[n+i];
        if(b[n-i] == b[n-i-1]) return b[n-i];
    }
    return b[1];
}

int main()
{
    freopen("pyramid.in", "r", stdin);
    freopen("pyramid.out", "w", stdout);
    
    T = read();
    while(T--)
    {
        n = read();
        l = 1, r = 2 * n - 1; 
        for(int i=l; i<=r; i++) a[i] = read();
        while(l < r)
        {
            int mid = (l + r + 1) >> 1;
            if(check(mid)) l = mid;
            else r = mid - 1;
        }
        printf("%d\n", l);
    }
    

    return 0;
}

code小于等于设为1
// ubsan: undefined
// accoders
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 5;

int T, l, r, n, a[maxn], b[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;
}

bool check(int mid)
{
    for(int i=1; i<=2*n-1; i++)
    {
        b[i] = (a[i] <= mid);//如果偏要变号呢?
    }
    for(int i=0; i<=n-1; i++)
    {
        if(b[n+i] == b[n+i+1]) return b[n+i];
        if(b[n-i] == b[n-i-1]) return b[n-i];
    }
    return b[1];
}

int main()
{
    freopen("pyramid.in", "r", stdin);
    freopen("pyramid.out", "w", stdout);
    
    T = read();
    while(T--)
    {
        n = read();
        l = 1, r = 2 * n - 1; 
        for(int i=l; i<=r; i++) a[i] = read();
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(check(mid)) r = mid;
            else l = mid + 1;
        }
        printf("%d\n", l);
    }
    

    return 0;
}

 

有一个类似的题,好像当初我也是想了半天二分的逻辑

排序:from 来自学长的馈赠5

当时的总结:二分的模板有两个,from进阶指南,一个是找满足条件的里尽量大的,另一个是找满足条件的里尽量小的,我本来以为要找大于等于的第一个,所以要用求尽量小的答案的模板,但是它不对,然后我忽然发现我枚举的是比大于等于当前数的标准,然后只要当前数在大于等于的范围内是符合条件,所以标准越小越容易满足条件,但是要找恰好的位置,所以这个大于等于的标准应该恰好卡死,也就是说其实要找最大的答案。
忽然发现这两次都遇到了一个概念,那就是我二分的这个标准到底是什么东西,二分的就是答案呀!
可以理解成:二分,找到一个尽量大的标准(>=某数)把当前查找的位置上的数包括进去,也可以理解成,二分当前位置的值是什么,如果推出矛盾就不合法
如果把大于等于mid的看成了1,如果这个mid合法就说明答案大于等于mid,扩大就是很自然的了
二分什么的。。不禁联想到了交互题?!

 

环游

搬个题解:V只会变化log2V次,每一层中可达范围形成若干个连续段,我们需要在每一层选一个连续段覆盖所有点。
状压dp,令preS表示当前选了S这些层,可以覆盖的最长前缀,l[i][j]表示长度满足第i层限制右端点在j的最大连续段的左端点的位置。
就是换一个角度考虑问题:
从每一个状态至多选一个,覆盖所有区间,可以是状态连续,区间跳来跳去,也可以是区间连续而状态自由组合。为什么可以跳?反正都是每个区间选一个,顺序并不重要啦。

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

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 3;

int n, v, x[maxn], l[19][maxn], r[19][maxn], pre[1<<19], suf[1<<19];

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()
{
    freopen("tour.in", "r", stdin);
    freopen("tour.out", "w", stdout);
    
    n = read(); v = read();
    for(int i=1; i<=n; i++) x[i] = read();
    int mx = log2(v)+1;//关于为什么写作__lg(),我不知道,上百度没查着。。
    for(int i=0; i<=mx; i++)
    {
        l[i][1] = 1; r[i][n] = n;
        //l[]以j为右端点的段,左端点最长延伸到哪里
        for(int j=1+1; j<=n; j++) if(x[j]-x[j-1]<=(v>>i)) l[i][j] = l[i][j-1]; else l[i][j] = j;
        for(int j=n-1; j>=1; j--) if(x[j+1]-x[j]<=(v>>i)) r[i][j] = r[i][j+1]; else r[i][j] = j;
    }
    memset(suf, 0x3f, sizeof(suf));
    suf[0] = n + 1;
    for(int i=0; i<(1<<mx); i++)
    {
        for(int j=1; j<=mx; j++)
        {
            if((i>>(j-1))&1) continue;
            pre[i|(1<<(j-1))] = max(pre[i|(1<<(j-1))], r[j][min(pre[i]+1, n)]);
            suf[i|(1<<(j-1))] = min(suf[i|(1<<(j-1))], l[j][max(suf[i]-1, 1)]);
        }
    }
    int cnt = 0;
    for(int i=1; i<=n; i=r[0][i]+1) cnt++;
    if(cnt > mx + 1)
    {
        for(int i=1; i<=n; i++) printf("Impossible\n");
    }
    else 
    {
        for(int i=1; i<=n; i=r[0][i]+1)
        {
            bool fl = false;
            for(int j=0; j<(1<<mx)&&!fl; j++) if(pre[j] >= i-1 && suf[((1<<mx)-1)^j] <= r[0][i]+1) fl = 1;
            for(int j=i; j<=r[0][i]; j++) if(fl) printf("Possible\n"); else printf("Impossible\n");
        }
    }

    return 0;
}

 

二择

审错题了,还以为选任意非空集合,那还没重边没自环,不就直接输出1?。。。
今天的官方题解写的真清楚:大小为n的匹配和大小为n的独立集加起来共3n个点,先求出一组极大匹配剩下来的点一定是独立集,两个要求中至少满足了一个。

code
/*
额,n个点或n条边,是我xia了
*/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1500005;

int T, n, m;
struct edge {int u, v;}e[maxn];
bool vis[maxn];
vector<int> ans;

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()
{
    freopen("choice.in", "r", stdin);
    freopen("choice.out", "w", stdout);
    
    T = read();
    while(T--)
    {
        n = read(), m = read();
        for(int i=1; i<=m; i++)
        {
            int u = read(), v = read();
            e[i] = {u, v};
        }
        ans.clear();
        for(int i=1; i<=n+n+n; i++) vis[i] = false;
        for(int i=1; i<=m; i++)
        {
            int u = e[i].u, v = e[i].v;
            if(vis[u] || vis[v]) continue;
            vis[u] = vis[v] = 1;
            ans.push_back(i);
        }
        if(ans.size() >= n)
        {
            printf("Beta2\n");
            for(int i=0; i<n; i++) printf("%d ", ans[i]);
            printf("\n"); continue;
        }
        ans.clear();
        for(int i=1; i<=n+n+n; i++) if(!vis[i]) ans.push_back(i);
        printf("Beta1\n");
        for(int i=0; i<n; i++) printf("%d ", ans[i]);
        printf("\n");
    }

    return 0;
}
posted @ 2022-10-27 17:36  Catherine_leah  阅读(30)  评论(0编辑  收藏  举报
/* */