NOIP多校联考4

B.虚弱(weakness)

当x增加的时候,答案应该是一个先减小再增大的过程,也就是说答案关于x是一个单峰函数,对于单峰函数我们可以用三分法求得极值。

work函数找到最大的修改后的前缀和和最小的修改后的前缀和,相减就是x为特定值时的答案。

#include <bits/stdc++.h>

using namespace std;

typedef long double ll;
const int maxn = 2e5 + 2;
const ll mod = 998244353;
const int INF = 0x7ffffff;

ll a[maxn], ans, sum[maxn];
int n;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        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 flag = a[1];
    for(int i=1; i<=n; i++)
    {
        if(flag != a[i]) return false;
    }
    return true;
}

ll work(ll sub)
{
    ll final_max = 0, final_min = 0;
    ll now_max = 0, now_min = 0;
    for(int i=1; i<=n; i++)
    {
        now_max += a[i] - sub;
        now_min += a[i] - sub;
        if(now_max > final_max) final_max = now_max;
        if(now_min < final_min) final_min = now_min;
        if(now_max < 0) now_max = 0;
        if(now_min > 0) now_min = 0;
    }
    return final_max > -final_min ? final_max : -final_min;
}

int main()
{
    freopen("weakness2.in", "r", stdin);
    
    n = read();
    for(int i=1; i<=n; i++)
    {
        scanf("%Lf", &a[i]);
    }
    if(check())
    {
        printf("%.6lf", 0.0); exit(0);
    }
    ll l = -10000, r = 10000;
    ll vall = work(l), valr = work(r);
    //printf("%.6lf %.6lf\n", vall, valr);
    while(r - l > 1e-13)
    {
        ll mid1 = l + (r - l) / 3;
        ll mid2 = r - (r - l) / 3;
        //printf("%.2lf %.2lf\n", mid1, mid2);
        ll val1 = work(mid1);
        ll val2 = work(mid2);

        if(vall >= val1)
        {
            if(val1 >= val2)
            {
                if(val2 >= valr)
                {
                    l = mid2; vall = val2;
                }
                else 
                {
                    l = mid1; vall = val1;
                }
            }
            else 
            {
                r = mid2; valr = val2;
            }
        }
        else 
        {
            r = mid1; valr = val1;
        }
    }
    printf("%.6Lf", vall);
    
    return 0;
}
感谢Silver_ash

C.萨鲁曼的半兽人(orc)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;
const ll mod = 998244353;
const int INF = 0x7ffffff;

int v[30], c, n;
char s[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        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()
{
    while(scanf("%s", s+1)!=EOF)
    {
        memset(v, 0, sizeof(v));
        c = 0;
        
        n = strlen(s+1);
        for(int i=1; i<=n; i++)
        {
            if(v[s[i]-'a']) 
            {
                c = i - v[s[i]-'a'];
                break;
            }
            v[s[i]-'a'] = i;
        }
        if(c == 0 || c > 2)
        {
            printf("%d\n", n-1);
        }
        else if(c == 1)
        {
            printf("%d\n", n-2);
        }
        else if(c == 2)
        {
            printf("%d\n", n-3);
        }
    }
    
    return 0;
}
赛时10分

这道题的原型是BZOJ3790神奇项链

https://blog.csdn.net/try__jhf/article/details/82973661题解

https://blog.csdn.net/rwbyblake/article/details/107991949manacher

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ll;
const int maxn = 2e5 + 2;
const ll mod = 998244353;
const int INF = 0x7ffffff;

int n, len, p[maxn], ans;
char s[maxn], st[100005];

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

struct data
{
    int L, R;
    data(int x=0, int y=0) {L = x-y+1; R = x+y-1;}
    bool operator < (const data &b) const 
    {
        if(L == b.L) return R < b.R;
        return L < b.L;
    }
}a[maxn];

void work()
{
    int mx = 0, id = 0;
    for(int i=1; i<=n; i++)
    {
        if(mx > i)
        {
            p[i] = min(p[2*id-i], mx-i);
            //printf("cmp : %d %d\n", p[2*id-i], mx-i);
        }
        else p[i] = 1;
        while(i-p[i] >= 1 && i+p[i]<=n && s[i-p[i]]==s[i+p[i]])
        {
            p[i]++;
        }
        if(mx < i+p[i])
        {
            mx = i+p[i]; id = i;
        }
        a[i] = data(i, p[i]);
        //printf("p[%d] = %d\n", i, p[i]);
        //printf("a[%d].L = %d a[%d].R = %d\n", i, a[i].L, i, a[i].R);
    }
}

int main()
{
    freopen("orc2.in", "r", stdin);
    
    while(scanf("%s", st+1)!=EOF)
    {
        len = strlen(st+1); n = 1; s[1] = '#'; ans = -1;
        for(int i=1; i<=len; i++)
        {
            s[++n] = st[i]; s[++n] = '#';
        }
        work();
        sort(a+1, a+1+n);
        for(int i=1,j=1,mx=0; i<=n; )
        {
            while(j<=n && a[j].L<=i)
            {
                mx = max(mx, a[j++].R);
            }
            i = mx + 1; ans++;
        }
        printf("%d\n", ans);
    }
    
    return 0;
}
View Code

 D.序列(seq)

这道题的重点就是:明白最后剩下的数会把区间分成三部分,然后把大区间拆成小区间。不过用优先队列来处理拼接的顺序,用RMQ来找区间最值,把奇数位置和偶数位置分开顺便填上1e6……细节还是很有趣的。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 2;
const ll mod = 998244353;
const int INF = 0x7ffffff;

struct node 
{
    int l, r, lm, rm;
    bool operator < (const node & T) const 
    {
        return l > T.l;
    }
}po;
priority_queue<node> s;//从小到大排个序
int ji[maxn][22], ou[maxn][22];
int n, st[maxn], top, pos[maxn];

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

//倍增维护区间最小值,或者说称为ST表?我还是习惯叫它RMQ……
void init()
{
    for(int j=1; j<=20; j++)
    {
        for(int i=1; i<=n; i++)
        {
            if(i+(1<<j)-1>n) continue;
            ji[i][j] = min(ji[i][j-1], ji[i+(1<<(j-1))][j-1]);
            ou[i][j] = min(ou[i][j-1], ou[i+(1<<(j-1))][j-1]);
        }
    }
}

inline int Get_1(int L, int R)
{
    int len = log2(R-L+1);//用一个while循环效果一样
    return min(ji[L][len], ji[R-(1<<len)+1][len]);
}

inline int Get_0(int L, int R)
{
    int len = log2(R-L+1);
    return min(ou[L][len], ou[R-(1<<len)+1][len]);
}

inline void putin(int L, int R)
{
    //printf("L = %d R = %d\n", L, R);
    node poo;
    if(L >= R) return;//这就不合法了(不用选了)
    if(L == R-1)//这就没得选了
    {
        if(L & 1)
        {
            poo.l = ji[L][0]; poo.r = ou[L+1][0];
        }
        else 
        {
            poo.l = ou[L][0], poo.r = ji[L+1][0];
        }
        poo.lm = L; poo.rm = R;
        s.push(poo);
        return;
    }
    if(L & 1)
    {
        poo.l = Get_1(L, R);
        poo.r = Get_0(pos[poo.l]+1, R);
    }
    else 
    {
        poo.l = Get_0(L, R);
        poo.r = Get_1(pos[poo.l]+1, R);
    }
    poo.lm = L, poo.rm = R;
    s.push(poo);
}

int main()
{
    n = read(); 
    bool is = 1;
    for(int i=1; i<=n; i++)
    {
        if(i & 1)
        {
            ji[i][0] = read(); ji[i+1][0] = 1e6; pos[ji[i][0]] = i;
        }
        else 
        {
            ou[i][0] = read(); ou[i-1][0] = 1e6; pos[ou[i][0]] = i;
        }
        if(ji[i][0] != i && ou[i][0] != i) is = 0;
    }
    if(is)
    {
        for(int i=1; i<=n; i++)
        {
            printf("%d ", i);
        }
        exit(0);
    }
    //printf("111");
    init();
    //printf("111");
    po.l = Get_1(1, n);
    //为什么新串中第一个一定要取奇数位?(我考试时就是因为没想到这个问题连暴搜都不会了)
    //因为要让剩下的数成为前两个,取剩下的数时一定不能跨过最后取的数,相当于划两道分界线
    //不是吧又是三分?
    po.r = Get_0(pos[po.l]+1, n);//然后从剩下的偶数位里找到一个搭配就好
    po.lm = 1; po.rm = n;//就是左右边界
    s.push(po);
    while(!s.empty())
    {
        po = s.top();
        s.pop();
        st[++top] = po.l, st[++top] = po.r;//好嘛,刚出一个又进一个
        putin(po.lm, pos[po.l]-1);
        putin(pos[po.l]+1, pos[po.r]-1);
        putin(pos[po.r]+1, po.rm);
    }
    for(int i=1; i<=top; i++)
    {
        printf("%d ", st[i]);
    }
    
    return 0;
}
View Code

 

posted @ 2022-07-22 21:46  Catherine_leah  阅读(40)  评论(1编辑  收藏  举报
/* */