[bzoj2962]序列操作

题目大意

你需要兹瓷对序列的三种操作:
1、区间加上一个数。2、区间取相反数。3、区间任意取k个数的积的和(k<=20)

线段树

我们需要维护两个标记,当线段树要维护多个标记时,如何处理标记之间的影响和如何下传标记成了问题。
这里推荐一下来自Fuxey的处理标记的有效原则
先看另一道题有趣的有趣的家庭菜园
在那道题中,线段树需要同时维护取max和加法标记。我们规定的执行顺序是先做取max再做加法。
没错,解决多标记问题,我们需要定一个标记优先级与执行顺序。那么每当要打一个新标记时,都要考虑对其执行顺序后的标记影响。处理好这一点就能解决问题。
同时,下传标记时,也应当按照执行顺序下传标记。
常见的标记还有set和add,执行顺序是先set再add。
JZOJ上还有一道题需要维护乘法和加法标记,我没有打,这题是3469,cty大爷写了题解数列
本题中,执行顺序是先取相反数再加法。
因为k<=20,可以想到保存dp数组f[i]表示在当前区间选取i个的积的和。
那么,当进行取相反操作时,因为其执行优先级比加法标记高,所以要将加法标记也取反。然后,对f的影响显然是 f[i](1)i
加法操作呢?来考虑f[i]的改变。
设加的是x,区间长度是len。
那么大概是一个(…+x)(…+x)……(…+x)
拆括号,枚举含有j个x,那么就是 xjf[ij]Cjleni+j
那个组合数的含义?因为有j个x,这j个x所在括号另一项的可能性是什么呢?i-j个已经确定,还有len-i+j个未确定,要选出j个。
于是两个操作都解决了。
现在,我们还需要一个合并操作——将两个区间信息合并。
也很容易,枚举分别选i个j个相乘加进i+j里。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=50000+10,mo=19940417;
struct dong{
    int f[21],len;
} tree[maxn*4];
int ad[maxn*4];
bool bz[maxn*4];
int a[maxn],c[maxn][21];
int i,j,k,l,t,n,m;
char ch;
dong ans;
void merge(dong a,dong b,dong &c){
    c.len=a.len+b.len;
    int i,j;
    fo(i,0,20) c.f[i]=0;
    fo(i,0,min(a.len,20))
        fo(j,0,min(b.len,20))
            if (i+j<=20) c.f[i+j]=(c.f[i+j]+(ll)a.f[i]*b.f[j]%mo)%mo;
}
void add(int p,int x){
    int i,j,k;
    fd(i,min(tree[p].len,20),0){
        k=x;
        fd(j,i-1,0){
            tree[p].f[i]=(tree[p].f[i]+(ll)tree[p].f[j]*c[tree[p].len-j][i-j]%mo*k%mo)%mo;
            tree[p].f[i]=(tree[p].f[i]+mo)%mo;
            k=(ll)k*x%mo;
        }
    }
    ad[p]=(ad[p]+x)%mo;
}
void rev(int p){
    int i;
    fo(i,0,min(tree[p].len,20)){
        tree[p].f[i]*=(i%2)?-1:1;
        tree[p].f[i]=(tree[p].f[i]+mo)%mo;
    }
    bz[p]^=1;
    ad[p]=-ad[p];
}
void down(int p){
    if (bz[p]){
        rev(p*2);
        rev(p*2+1);
        bz[p]=0;
    }
    if (ad[p]){
        add(p*2,ad[p]);
        add(p*2+1,ad[p]);
        ad[p]=0;
    }
}
void change1(int p,int l,int r,int a,int b,int x){
    if (l==a&&r==b){
        add(p,x);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) change1(p*2,l,mid,a,b,x);
    else if (a>mid) change1(p*2+1,mid+1,r,a,b,x);
    else{
        change1(p*2,l,mid,a,mid,x);
        change1(p*2+1,mid+1,r,mid+1,b,x);
    }
    merge(tree[p*2],tree[p*2+1],tree[p]);
}
void change2(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        rev(p);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) change2(p*2,l,mid,a,b);
    else if (a>mid) change2(p*2+1,mid+1,r,a,b);
    else{
        change2(p*2,l,mid,a,mid);
        change2(p*2+1,mid+1,r,mid+1,b);
    }
    merge(tree[p*2],tree[p*2+1],tree[p]);
}
void build(int p,int l,int r){
    if (l==r){
        tree[p].len=1;
        tree[p].f[0]=1;
        tree[p].f[1]=a[l];
        return;
    }
    int mid=(l+r)/2;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    merge(tree[p*2],tree[p*2+1],tree[p]);
}
void query(int p,int l,int r,int a,int b){
    if (l==a&&r==b){
        merge(ans,tree[p],ans);
        return;
    }
    down(p);
    int mid=(l+r)/2;
    if (b<=mid) query(p*2,l,mid,a,b);
    else if (a>mid) query(p*2+1,mid+1,r,a,b);
    else{
        query(p*2,l,mid,a,mid);
        query(p*2+1,mid+1,r,mid+1,b);
    }
}
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*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char get(){
    char ch=getchar();
    while (ch!='Q'&&ch!='I'&&ch!='R') ch=getchar();
    return ch;
}
int main(){
    freopen("sequence8.in","r",stdin);
    n=read();m=read();
    c[0][0]=1;
    fo(i,1,n){
        c[i][0]=1;
        fo(j,1,20) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    }
    fo(i,1,n) a[i]=read();
    build(1,1,n);
    fo(i,1,m){
        ch=get();
        j=read();k=read();
        if (ch=='I'){
            l=read();
            change1(1,1,n,j,k,l);
        }
        else if (ch=='R') change2(1,1,n,j,k);
        else{
            l=read();
            ans.len=0;
            ans.f[0]=1;
            query(1,1,n,j,k);
            printf("%d\n",ans.f[l]);
        }
    }
}
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值