【被迫营业7】数形结合

numbergraph.pdf

Solution得等会。

Solution

  • (20\%,opt in {+,-})
    这个水,无论从哪个点开始的值都是一样的,只要随便找一个点跑一遍输出既可。
    (mathcal{O}(n))

  • (10\%,n le 1000)
    这个照样水,枚举起点然后暴力算出其(F(x))值,取最大既可。
    (mathcal{O}(n ^ 2))

  • (10\%,opt in {+, imes})
    这有啥用啊,反正我是没想到
    我瞎写的,不会真有人不会正解能拿这个部分分吧/jk/jk/jk

  • (mathcal{O}(nsqrt{n}))做法
    我们对边(E)集合分块,每个整块长度为(sqrt{n}),对每个块进行一下运算,变成一个(ax+b)的形式(也就是说一个数(x)进到这个块里运算的时候会变成(ax+b),相当于整个块的变换),跑一遍,总复杂度为(mathcal{O}(n))
    然后枚举起点,如果接下来的运算是一个完整的块,那么是(mathcal{O}(1))的,否则就暴力一下,最坏(mathcal{O}(sqrt{n}))
    显然是(mathcal{O}(nsqrt{n}))的复杂度,瓶颈在运算那边。

std使用了破环成链的方法,便于模拟。

# include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
struct edge
{
    char opt;
    long long x;
    int soc;
}E[N << 1];

struct node
{
    long long mul,add;
}a[N << 1]; 

int len,num;

int belong(int x)
{
    if(x <= n) return (x - 1) / len + 1;
    else 
    {
        x -= n;
        return (x - 1) / len + 1 + num;
    }
}
int l[1005],r[1005];

void update(long long &x,int i)
{
    if(E[i].opt == '+') x += E[i].x;
    else x *= E[i].x;
    return;
}

long long solve(int X)
{
    int now = X,cnt = 0;
    long long ans = 1;
    int R = X + n - 1;
    while(now <= R)
    {
        bool flag = 1;
        int _l,_r;
        if(now != l[belong(now)]) 
        {
            flag = 0;
            _l = now;
        }
        else _l = l[belong(now)];
        if(r[belong(_l)] > R)
        {
            _r = R;
            flag = 0;
        }
        else _r = r[belong(_l)];
        if(!flag)
        {
            for(int i = _l; i <= _r; i++)
            {
                update(ans,i);
            }
            now = _r + 1;
        }
        else
        {
            ans = ans * a[belong(_l)].mul + a[belong(_l)].add;
            now = _r + 1;
        }
    }
    return ans;

}

int main(void)
{
    freopen("exin.in","r",stdin);
    freopen("out1.out","w",stdout);
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    {
        cin >> E[i].opt;
        scanf("%lld",&E[i].x);
        if(E[i].opt == '-') 
        {
            E[i].opt = '+';
            E[i].x = -E[i].x;
        }
        E[i].soc = i;
        E[i + n] = E[i];
    }
    len = sqrt(n),num = (n - 1) / len + 1;
    for(int i = 1; i <= num; i++) 
    {
        l[i] = (i - 1) * len + 1;
        r[i] = i * len;
        a[i].mul = 1,a[i].add = 0;
    }
    r[num] = n;
    for(int i = num + 1; i <= 2 * num; i++)
    {
        l[i] = l[i - num] + n;
        r[i] = r[i - num] + n;
        a[i].mul = 1,a[i].add = 0;
    }
    for(int i = 1; i <= num * 2; i++)
    {
        for(int j = l[i]; j <= r[i]; j++)
        {
            if(E[j].opt == '+') 
            {
                a[i].add += E[j].x;
            }
            else 
            {
                a[i].mul *= E[j].x;
                a[i].add *= E[j].x;
            }
        }
    }
    long long ans = -1e9;
    for(int i = 1; i <= n; i++)
    {
        long long _s = solve(i);
        ans = max(ans,_s);
    }
    printf("%lld
",ans);
    return 0;
}
  • (mathcal{O}(nlog{n}))做法
    by zps(LightningUZ).

其实差不多,破环成链,其实(F(x))就等于(E_x)(E_{x + n})运算一遍,显然可以线段树维护求和。时间复杂度为(mathcal{O}(nlog{n}))

对于两个区间((mul_1,add_1),(mul_2,add_2))合并:

[mul_2(mul_1 x + add_1) + add_2\ = mul_1 mul_2 x + mul_2 add_1 + add_2 ]

所以新的(mul = mul_1mul_2,add = mul_2add_1+add_2)

# include <bits/stdc++.h>
using namespace std;
const int N = 100005;
struct node 
{
    long long mul,add;
}T[N << 2];
struct edge
{
    char opt;
    long long x;
}E[N << 1];
int n;

void build(int root,int l,int r)
{
    T[root].mul = 1;
    T[root].add = 0;
    if(l == r)
    {
        if(E[l].opt == 'x') 
        {
            T[root].mul *= E[l].x;
            T[root].add *= E[l].x;
        }
        else
        {
            T[root].add += E[l].x;
        }
        return;
    }
    int mid = (l + r) >> 1;
    build(root << 1,l,mid);
    build(root << 1 | 1,mid + 1,r);
    T[root].mul = T[root << 1].mul * T[root << 1 | 1].mul;
    T[root].add = T[root << 1].add * T[root << 1 | 1].mul + T[root << 1 | 1].add;
    if(T[root].mul == 0) T[root].mul = 1;
    return;
}

node query(int root,int l,int r,int s,int t)
{
    if(l <= s && t <= r)
    {
        return T[root];
    }
    int mid = (s + t) >> 1;
    node L,R;
    L.mul = 1,L.add = 0;
    R.mul = 1,R.add = 0;
    if(l <= mid) L = query(root << 1,l,r,s,mid);
    if(r > mid) R = query(root << 1 | 1,l,r,mid + 1,t);
    if(L.mul == 0) L.mul = 1;
    if(R.mul == 0) R.mul = 1;
    node ans;
    ans.mul = 1,ans.add = 0;
    ans.mul = L.mul * R.mul;
    ans.add = L.add * R.mul + R.add;
    // printf("lmul = %lld,ladd = %lld,rmul = %lld,radd = %lld
",L.mul,L.add,R.mul,R.add);
    // printf("ansmul = %lld,ansadd = %lld
",ans.mul,ans.add);
    return ans;
}

void print(int root,int l,int r)
{
    int mid = (l + r) >> 1;
    if(l != r) print(root << 1,l,mid);
    if(l != r) print(root << 1 | 1,mid + 1,r);
    printf("T[%d] : mul = %lld,add = %lld,l = %d,r = %d
",root,T[root].mul,T[root].add,l,r);
}

int main(void)
{
    freopen("exin.in","r",stdin);
    freopen("out2.out","w",stdout);
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)
    {
        cin >> E[i].opt;
        scanf("%lld",&E[i].x);
        if(E[i].opt == '-') 
        {
            E[i].opt = '+';
            E[i].x = -E[i].x;
        }
    }
    for(int i = 1; i <= n; i++)
    {
        E[i + n] = E[i];
    }
    for(int i = 1; i <= (n << 2); i++) 
    {
        T[i].mul = 1,T[i].add = 0;
    }   
    build(1,1,n + n);
    // print(1,1,2 * n);
    long long ans = -1e9;
    for(int i = 1; i <= n; i++)
    {
        node A = query(1,i,i + n - 1,1,n * 2);
        // printf("i = %d,mul = %lld,add = %lld
",i,A.mul,A.add);
        ans = max(ans,A.mul + A.add);
    }
    printf("%lld
",ans);
    return 0;
}
  • (mathcal{O}(n))做法
    by zps(LightningUZ).

单调区间求和trick,具体私聊zps.

总结:ZPS就是强啊!

原文地址:https://www.cnblogs.com/luyiming123blog/p/14315062.html