Noi 十连测 基因改造计划

SOL:

我们跑马拉车算法,然后写主席树维护。

#include<bits/stdc++.h>
#define LL long long
#define M 6000007
#define N 260010
#define Mid (l+r>>1)
using namespace std;
LL allsf,allsi,allcnt;
int n,m;
#define sight(c) (‘0‘<=c&&c<=‘9‘)
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar())x=x*10+c-48;
}
void write(LL x){if (x<10) {putchar(‘0‘+x); return;} write(x/10); putchar(‘0‘+x%10);}
inline void writeln(LL x){ if (x<0) putchar(‘-‘),x*=-1; write(x); putchar(‘\n‘); }
inline void writel(LL x){ if (x<0) putchar(‘-‘),x*=-1; write(x); putchar(‘ ‘); }
struct Tree{
    struct Node{
        int lc,rc,cnt;
        LL sf,si;
    }T[M]; int rot[N],tot;
    int x,f,i,L,R;
    void add(int past,int &now,int l,int r) {
        now=++tot; T[now]=T[past]; T[now].sf+=f; T[now].si+=i; T[now].cnt++;
        if (l==r) return;
        if (x<=Mid) add(T[past].lc,T[now].lc,l,Mid);
        else add(T[past].rc,T[now].rc,Mid+1,r);
    }
    void que(int past,int now,int l,int r){
        if (L<=l&&r<=R) {
            allsf+=(T[now].sf-T[past].sf); allsi+=T[now].si-T[past].si;
            allcnt+=T[now].cnt-T[past].cnt; return;
        }
        if (L<=Mid) que(T[past].lc,T[now].lc,l,Mid);
        if (R> Mid) que(T[past].rc,T[now].rc,Mid+1,r);
    }
    void adds(int now,int X,int F,int I){
        x=min(X,n+1); f=F; i=I; add(rot[now-1],rot[now],0,n+1);
    }
    void ques(int l,int r,int LLL,int RR){
        allsf=allsi=allcnt=0; L=LLL; R=RR;
        que(rot[l-1],rot[r],0,n+1);
    }
}T1,T2;
char s[N],ch[N];
int f[N],k=1,l,r;
LL ans;
void mach(){
    for (int i=n;i;i--)
     s[i<<1]=ch[i],s[i*2-1]=‘&‘;
    n=n<<1|1; s[n]=‘&‘; s[0]=‘{‘;
    f[1]=0;
    for (int i=2;i<=n;i++) {
        if (k+f[k]>=i)
              f[i]=min(k+f[k]-i,f[2*k-i]);
        else f[i]=-1;
        while(s[i+f[i]+1]==s[i-f[i]-1]) ++f[i];
        if(i+f[i]>=k+f[k]) k=i;
    }
    for(int i=1;i<=n;++i){
        T1.adds(i,i-f[i],f[i],i);
        T2.adds(i,i+f[i],f[i],i);
    }
}
signed main () {
//    freopen()
    freopen("gene.in","r",stdin);
    freopen("gene.out","w",stdout);
    read(n); read(m);
    scanf("%s",ch+1);
    mach();
    while (m--){
        read(l); read(r);
        ans=r-l+1;
        l=l*2-1; r=r<<1|1;
        T1.ques(l,Mid,0,l); ans+=allsi-l*allcnt;
//        writel(allsi); writel(allcnt);
//        writeln(ans);
        T1.ques(l,Mid,l+1,n+1); ans+=allsf;

        T2.ques(Mid+1,r,0,r); ans+=allsf;
        T2.ques(Mid+1,r,r+1,n+1); ans+=r*allcnt-allsi;
        writeln(ans>>1);
    } return 0;
}

原文地址:https://www.cnblogs.com/rrsb/p/8666176.html

时间: 2025-01-15 06:16:47

Noi 十连测 基因改造计划的相关文章

NOI十连测 第五测 T1

1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<algorithm> 6 int f[257][257],n,type,V[257],g[257][257],ans,cnt; 7 char op[204]; 8 int read(){ 9 int t=0,f=1;char ch=getchar(); 10 while

[NOI十连测] 二进制的世界

题意 给定一个长度为 n 的序列 A , 以及一种位运算 Op . 对于每个位置 x , 求 $\min_{y < x} A[y] ~ Op ~ A[x]$ . n <= 100000, 0 <= A[i] <= 65536 . 一个高效的Trick 我们可以贪心地尽可能使高的位大. g[x] 表示前 8 位为 x 的一个 vector . 我们维护 g 进行剪枝. 1 #include <cstdio> 2 #include <cstring> 3 #in

NOI十连测 第四测 T3

思路: 算法一:可以n^2找出每个点的权值,然后n^2做完,预计得分10 算法二:随机找点然后每次找最高..貌似只有10分?然而考试的时候煞笔了,边界设成inf.. 算法三:随机找几个点,然后随机爬山,听说有50~70 算法四:考虑将列分治,每次分成2部分,找出每部分边界的最大值,判断最大值左边和右边的大小,然后往大的那一边递归,预计得分70 算法五:不仅将列分治,并且将行分治,将一个n*n的矩阵划分,然后递归找,预计得分100 1 #include<bits/stdc++.h> 2 #inc

NOI十连测 第四测 T1

思路:首先每个蚂蚁移速相同,而且碰到就转头,这其实等价于擦肩而过! 看到2n个数互不相同就觉得方便多了:枚举每个数字往左或者往右作为最慢,然后考虑其他蚂蚁有多少种走路方向. (1),走的距离大于m/2 假如红色描述的是一个蚂蚁的移动轨迹,那么蓝色部分左边的蚂蚁只能向左走,蓝色右边的蚂蚁只能向右走. 而蓝色部分中的蚂蚁可以向左也可以向右,方案数为2^n,n为蓝色部分蚂蚁数量. (2),走的距离小于m/2 如图,则蓝色部分左边的蚂蚁只能向左,蓝色部分右边的蚂蚁只能向右.而蓝色部分中间不能有蚂蚁!,这

NOI十连测 DAY3 T1

这么二逼的题考试的时候我想了好久,我真是太弱了... 首先,由于ans都乘上了i*(i-1)/2,实际上要求的就是每个数的所有可能出现次数*这个数的权值. 我们发现,每个数的本质是一样的,我们记一个sum为数的总和,这样只要统计一次就OK了. 我们把每次的选择抽象成有向边,每个状态视为点,这样就构成一个有根树. 如图 我们只考虑1对答案的贡献.如图,在每层计算当前合并对答案的贡献,也就是要能得知我在这个节点选择合并1或者1的联通块,那么我能覆盖到几个叶子节点? 那么就变成O(n)的组合数学题了.

BZOJ NOI十连测 第一测 T2

思路:看到这题,就感觉是一道很熟悉的题目: http://www.cnblogs.com/qzqzgfy/p/5535821.html 只不过这题的K最多可以到N,而且边权不再只是1,考试的时候yy了一下做法: 找k次直径,第一次把边取反,要是第二次再取到同样的边,那就把它变成0,毕竟每条边只经过2次嘛,YY的很好,实际上,交上去,5分TAT 后来听以为神犇说不是取0,而是继续取反,每条边取一次就取反一次,woc.. 1 #include<cstdio> 2 #include<cmath

NOI十连测 第五测 T2

思路:考虑建立可持久化线段树,第一层维护的是i这个位置的next位置,第二层,维护的是接下来走这个字符会到哪个节点. 感觉很巧妙啊,不愧是Claris 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 int v[18000005],l[18000005],r[18000005],sz; 7 i

BZOJ NOI十连测 第一测 T1

思路:首先考虑t=1的情况,t等于1,那么所有位置的颜色相同,我们不用考虑概率的问题,那么,k+d*x在模d下都相等,我们考虑预处理一个数组s[i][j],代表d为i,起始位置为j的等差数列的和,这个可以证明,当模小于等于sqrt(n)的时候可以完美解决,时间复杂度为N^1.5,对于d大于sqrt(n)的情况,只需要暴力枚举就可以了. 再考虑t>=2的情况,我们选的颜色一定是颜色数最少的那个,颜色数最少的颜色的期望绝对是最小的,然后,我们分k的左边和k的右边进行计算,我们这里称呼k+d*x的位置

NOI十连测 第六测 T1

思路: 用treap动态维护,记一个sum1,sum2,注意!,写treap如果有删除操作,千万不能把权值相同的分开来..,这在删除的时候会进入死循环,这是一个惨痛的教训... 1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<time.h> 7 #define l