树套树

之前写的线段树常用方法

概念
注:参考博客里最后一句话(多行修改很难实现,因为x树的Lazy-tag是区间,无法合并)仅针对x树,y树依然可以区间修改(具体原因下文会结合题目说明)

HDU4819

原题地址
题目大意:给出一个nn的矩阵,给出m个询问,每个询问形如x,y,z,表示询问以x,y为中心点的zz子矩阵的最大值MAX和最小值MIN,输出floor((MIN+MAX)/2)并将x,y修改为floor((MIN+MAX)/2)(floor是向下取整)。

代码:
代码来自于参考博客

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=800,maxt=4*maxn,MAXINT=((1<<30)-1)*2+1;

int te,n,m,pic[maxn+5][maxn+5];
//===========================================================
struct SegmentTreexy //线段树套线段树
{
    int MIN[maxt+5][maxt+5],MAX[maxt+5][maxt+5];
    int posx,posy,Lx,Rx,Ly,Ry,fa,k;bool is_leaf; //fa是当前x树,is_leaf表示fa是否是叶节点
    void Pushupy(int fa,int p) //更新列
    {
        MIN[fa][p]=min(MIN[fa][p<<1],MIN[fa][p<<1|1]);
        MAX[fa][p]=max(MAX[fa][p<<1],MAX[fa][p<<1|1]);
    }
    void Pushupx(int fa,int p) //更新行
    {
        MIN[fa][p]=min(MIN[fa<<1][p],MIN[fa<<1|1][p]);
        MAX[fa][p]=max(MAX[fa<<1][p],MAX[fa<<1|1][p]);
    }
    void Buildy(int p,int L,int R,int *a) //构造列
    {
        if (L==R) {if (is_leaf) MIN[fa][p]=MAX[fa][p]=a[L]; else Pushupx(fa,p);return;}
        //如果是fa叶节点就直接构造,否则更新
        int mid=L+(R-L>>1);
        Buildy(p<<1,L,mid,a);Buildy(p<<1|1,mid+1,R,a);
        Pushupy(fa,p);
    }
    void Buildx(int p,int L,int R) //构造行
    {
        if (L==R) fa=p,is_leaf=true,Buildy(1,Ly,Ry,pic[L]); else
        {
            int mid=L+(R-L>>1);
            Buildx(p<<1,L,mid);Buildx(p<<1|1,mid+1,R);
            fa=p;is_leaf=false;Buildy(1,Ly,Ry,pic[L]);
        }
    }
    void Inserty(int p,int L,int R) //更新列
    {
        if (posy<L||R<posy) return;
        if (L==R) {if (is_leaf) MIN[fa][p]=MAX[fa][p]=k; else Pushupx(fa,p);return;}
        //如果是fa叶节点就直接修改,否则更新
        int mid=L+(R-L>>1);
        Inserty(p<<1,L,mid);Inserty(p<<1|1,mid+1,R);
        Pushupy(fa,p);
    }
    void Insertx(int p,int L,int R) //更新行
    {
        if (posx<L||R<posx) return;
        if (L==R) fa=p,is_leaf=true,Inserty(1,1,n); else
        {
            int mid=L+(R-L>>1);
            Insertx(p<<1,L,mid);Insertx(p<<1|1,mid+1,R);
            fa=p;is_leaf=false;Inserty(1,1,n);
        }
    }
    int Askminy(int p,int L,int R) //在列中求MIN
    {
        if (Ry<L||R<Ly) return MAXINT;
        if (Ly<=L&&R<=Ry) return MIN[fa][p];
        int mid=L+(R-L>>1);
        return min(Askminy(p<<1,L,mid),Askminy(p<<1|1,mid+1,R));
    }
    int Askminx(int p,int L,int R) //在行中求MIN
    {
        if (Rx<L||R<Lx) return MAXINT;
        if (Lx<=L&&R<=Rx) {fa=p;return Askminy(1,1,n);}
        int mid=L+(R-L>>1);
        return min(Askminx(p<<1,L,mid),Askminx(p<<1|1,mid+1,R));
    }
    int Askmaxy(int p,int L,int R) //在列中求MAX
    {
        if (Ry<L||R<Ly) return -MAXINT;
        if (Ly<=L&&R<=Ry) return MAX[fa][p];
        int mid=L+(R-L>>1);
        return max(Askmaxy(p<<1,L,mid),Askmaxy(p<<1|1,mid+1,R));
    }
    int Askmaxx(int p,int L,int R) //在行中求MAX
    {
        if (Rx<L||R<Lx) return -MAXINT;
        if (Lx<=L&&R<=Rx) {fa=p;return Askmaxy(1,1,n);}
        int mid=L+(R-L>>1);
        return max(Askmaxx(p<<1,L,mid),Askmaxx(p<<1|1,mid+1,R));
    }
    void Build(int x_s,int y_s,int x_t,int y_t) {Lx=x_s;Rx=x_t;Ly=y_s;Ry=y_t;Buildx(1,Lx,Rx);}
    void Insert(int x,int y,int z) {posx=x;posy=y;k=z;Insertx(1,1,n);}
    int Ask(int x_s,int y_s,int x_t,int y_t)
    {
        Lx=x_s;Rx=x_t;Ly=y_s;Ry=y_t;
        return (Askminx(1,1,n)+Askmaxx(1,1,n))/2;
    }
};
SegmentTreexy tr;
//===========================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
    int tot=0,f=1;char ch=getchar(),lst=ch;
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    readi(te);
    for (int t=1;t<=te;t++)
    {
        printf("Case #%d:\n",t);
        readi(n);
        for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            readi(pic[i][j]);
        tr.Build(1,1,n,n);
        readi(m);
        while (m--)
        {
            int x,y,z;readi(x);readi(y);readi(z);z/=2;
            int Lx=x-z,Rx=x+z,Ly=y-z,Ry=y+z,now=tr.Ask(Lx,Ly,Rx,Ry);
            printf("%d\n",now);
            tr.Insert(x,y,now);
        }
    }
    return 0;
}

个人认为,在阅读代码的过程中,理解每个变量、数组的定义对理解和学习该算法十分重要。上文代码中,y树的叶子结点的意义与该树在x树中所对应结点的位置相关。若是x树叶子节点上的y树叶子节点,则该结点对应矩阵某一元素值;若是x树的非叶子结点,则对应某一列连续的几行,其示意图如下:
在这里插入图片描述
但我觉得修改x树非子树结点的MAX和MIN值貌似不一定从两棵子树向上推?毕竟只是单点修改而非区间修改。有空自己敲代码的时候试着改一改吧,明天必须肝嵌入式作业了。

3/11日更新:今天自己试着敲了一下,发现自己傻了,更新MAX值确实只能从下向上更新(因为更新父结点的时候不知道修改的叶子节点是不是原最大值)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 808
const int inf = 2e9 + 7;
int T, dp[maxn][maxn], ma[maxn << 2][maxn << 2], mi[maxn << 2][maxn << 2];
int n, m, le[maxn << 2];

void upx(int k, int f)
{
    ma[f][k] = max(ma[f << 1][k], ma[f << 1 | 1][k]);
    mi[f][k] = min(mi[f << 1][k], mi[f << 1 | 1][k]);
}

void upy(int k, int f)
{
    ma[f][k] = max(ma[f][k << 1], ma[f][k << 1 | 1]);
    mi[f][k] = min(mi[f][k << 1], mi[f][k << 1 | 1]);
}

void buildy(int k, int lx, int rx, int l, int r, int f)
{
    if (l == r)
    {
        if (!le[f])
        {
            upx(k, f);
        }
        else
        {
            ma[f][k] = dp[lx][l];
            mi[f][k] = dp[lx][l];
        }
        return;
    }
    int mid = (l + r) >> 1;
    buildy(k << 1, lx, rx, l, mid, f);
    buildy(k << 1 | 1, lx, rx, mid + 1, r, f);
    upy(k, f);
}

void buildx(int p, int l, int r)
{
    if (l == r)
    {
        le[p] = 1;
        buildy(1, l, r, 1, n, p);
        return;
    }
    int mid = (l + r) >> 1;
    buildx(p << 1, l, mid);
    buildx(p << 1 | 1, mid + 1, r);
    buildy(1, l, r, 1, n, p);
}

void chgy(int k, int l, int r, int f, int pos, int zh)
{
    if (l == r)
    {
        if (!le[f])
        {
            upx(k, f);
        }
        else
        {
            ma[f][k] = zh;
            mi[f][k] = zh;
        }
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) chgy(k << 1, l, mid, f, pos, zh);
    else chgy(k << 1 | 1, mid + 1, r, f, pos, zh);
    upy(k, f);
}

void chgx(int p, int l, int r, int posx, int posy, int zh)
{
    if (l == r)
    {
        chgy(1, 1, n, p, posy, zh);
        return;
    }
    int mid = (l + r) >> 1;
    if (posx <= mid) chgx(p << 1, l, mid, posx, posy, zh);
    else chgx(p << 1 | 1, mid + 1, r, posx, posy, zh);
    chgy(1, 1, n, p, posy, zh);
}

int query(int k, int lq, int rq, int l, int r, int f, int opt)
{
    if (lq <= l && rq >= r)
    {
        if (opt == 0) return mi[f][k];
        else return ma[f][k];
    }
    int mid = (l + r) >> 1;
    int maxx = -inf;
    int minn = inf;
    if (mid >= lq)
    {
        if (opt == 0) minn = min(minn, query(k << 1, lq, rq, l, mid, f, opt));
        else maxx = max(maxx, query(k << 1, lq, rq, l, mid, f, opt));
    }
    if (mid + 1 <= rq)
    {
        if (opt == 0) minn = min(minn, query(k << 1 | 1, lq, rq, mid + 1, r, f, opt));
        else maxx = max(maxx, query(k << 1 | 1, lq, rq, mid + 1, r, f, opt));
    }
    if (opt == 0) return minn;
    return maxx;
}

int queryx(int p, int lqx, int rqx, int lqy, int rqy, int l, int r, int opt)
{
    if (lqx <= l && rqx >= r)
    {
        return query(1, lqy, rqy, 1, n, p, opt);
    }
    int mid = (l + r) >> 1;
    int maxx = -inf;
    int minn = inf;
    if (lqx <= mid)
    {
        if (opt == 0) minn = min(minn, queryx(p << 1, lqx, rqx, lqy, rqy, l, mid, opt));
        else maxx = max(maxx, queryx(p << 1, lqx, rqx, lqy, rqy, l, mid, opt));
    }
    if (rqx >= mid + 1)
    {
        if (opt == 0) minn = min(minn, queryx(p << 1 | 1, lqx, rqx, lqy, rqy, mid + 1, r, opt));
        else maxx = max(maxx, queryx(p << 1 | 1, lqx, rqx, lqy, rqy, mid + 1, r, opt));
    }
    if (opt == 0) return minn;
    return maxx;
}

bool Eoln(char ch) { return ch == 10 || ch == 13 || ch == EOF; }
int readi(int& x)
{
    int tot = 0, f = 1; char ch = getchar(), lst = ch;
    while ('9' < ch || ch < '0') { if (ch == EOF) return EOF; lst = ch; ch = getchar(); }
    if (lst == '-') f = -f;
    while ('0' <= ch && ch <= '9') tot = tot * 10 + ch - 48, ch = getchar();
    x = tot * f;
    return Eoln(ch);
}

void init()
{
    for (int i = 0; i <= 4 * n; i++) le[i] = 0;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int i, j, x, r, cas = 0;
    readi(T);
    while (T--)
    {
        printf("Case #%d:\n", ++cas);
        readi(n);
        init();
        for (i = 1; i <= n; i++)
        {
            for (j = 1; j <= n; j++)
            {
                readi(dp[i][j]);
            }
        }
        buildx(1, 1, n);
        readi(m);
        while (m--)
        {
            int u, v, w;
            readi(u); readi(v); readi(w);
            w /= 2;
            int lqx = u - w;
            int rqx = u + w;
            int lqy = v - w;
            int rqy = v + w;
            int maxx = queryx(1, lqx, rqx, lqy, rqy, 1, n, 1);
            int minn = queryx(1, lqx, rqx, lqy, rqy, 1, n, 0);
            int zh = (maxx + minn) / 2;
            printf("%d\n", zh);
            chgx(1, 1, n, u, v, zh);
        }
    }
    return 0;
}

代码T了好几次,一开始以为是输入输出的问题,但用了剽来的快读板子后还是在T。后来发现,把所有的k*2改为k<<1后,瞬间AC。没想到这个优化节省的时间那么多(booth乘法和右移之间的效率差,本题可能有10倍差距)。
整个代码敲下来的手感和敲两棵线段树差不多。

POJ 2155

原题地址
题目大意:在n*n矩阵中每次对一个子矩阵进行翻转(0变1,1变0),然后多次询问某个点是0还是1。

代码:
本题只涉及求和,并且可以转化为单点修改(类似于差分),即修改区域(x1,y1)(x2,y2)内的值,则只须(x1,y1)+1,(x1,y2+1)-1,(x2+1,y1)-1,(x2+1,y2+1)+1,这样,对于每个点(x,y),矩阵(1,1)~(x,y)内的值即可表示其被反转的次数。
代码来自参考博客

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn = 1003;
char s[5];
int a[maxn][maxn], n;
int lowbit(int x) {
	return (x & (-x));
}
void update(int x, int y, int k) {
	for (int i = x; i <= n; i += lowbit(i)) {
		for (int j = y; j <= n; j += lowbit(j)) {
			a[i][j] += k;
		}
	}
}
int sum(int x, int y) {
	int res = 0;
	for (int i = x; i > 0; i -= lowbit(i)) {
		for (int j = y; j > 0; j -= lowbit(j))
			res += a[i][j];
	}
	return res;
}
int main() {
	int ca, t;
	int x, y, u, v;
	scanf("%d", &ca);
	while (ca--) {
		memset(a, 0, sizeof(a));
		scanf("%d%d", &n, &t);
		while (t--) {
			scanf("%s%d%d", s, &x, &y);
			if (s[0] == 'C') {
				scanf("%d%d", &u, &v);
				x++, y++;
				u++, v++;
				update(u, v, 1);
				update(x - 1, y - 1, 1);
				update(x - 1, v, -1);//这里有修改
				update(u, y - 1, -1);
			}
			else {
				printf("%d\n", sum(x, y) % 2);
			}
		}
		printf("\n");
	}
	return 0;
}

P3332 [ZJOI2013]K大数查询

原题地址

代码:
权值线段树套区间线段树模板题,权值线段树的每个叶子节点p代表有k[p]元素的集合有多少个。而修改时,由于只是统计含有元素p的元素的数量的变化,所以对线段树的更新即为某一区间的值+1。对于权值线段树的非叶子结点,可以理解为(1-n)集合内含有元素[l,r]的数量个数。
另外,本题的集合可以有重复元素。
由于存储空间的问题,本题采用了动态开点。rt[p]代表权值线段树的p结点所对应的区间线段树的根结点存储位置,ls[k]则代表区间线段树k结点的左儿子的存储位置,类似链式前向星的存储方法。
参考博客感觉是目前我最能看明白的代码,根据它的思路我自己写了写。由于其省略了区间线段树pushdown的过程,我用自己的线段树板子时进行了修改。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<map>
#include<bitset>
using namespace std;
typedef long long ll;
#define maxn 50005*4
#define msz 50005*16*16*2
ll sum[msz], tag[msz], m, n, tot = 0, k[maxn], cnt = 0, mmp[maxn];
int rs[msz], ls[msz], rt[maxn];
struct nod
{
    int id, opt;
    int ql, qr;
    ll num;
}qq[maxn];

struct sm
{
    int id;
    ll zh;
    bool operator <(sm u)
    {
        return zh < u.zh;
    }
}b[maxn];

void pushup(int k)
{
    sum[k] = sum[ls[k]] + sum[rs[k]];
}

void pushdown(int k, int l, int r)//区间线段树pushdown
{
    if (tag[k])
    {
        int mid = (l + r) >> 1;
        if (!ls[k]) ls[k] = ++tot;
        if (!rs[k]) rs[k] = ++tot;
        tag[ls[k]] += tag[k];
        tag[rs[k]] += tag[k];
        sum[ls[k]] = sum[ls[k]] + 1ll * (mid - l + 1) * tag[k];
        sum[rs[k]] = sum[rs[k]] + 1ll * (r - mid) * tag[k];
        tag[k] = 0;
    }
}

void add_sum(int& k, int l, int r, int ql, int qr)//区间线段树插入
{
    if (!k) k = ++tot;
    sum[k] = sum[k] + min(r, qr) - max(l, ql) + 1;
    if (l >= ql && r <= qr)
    {
        tag[k]++;
        return;
    }
    int mid = (l + r) >> 1;
    pushdown(k, l, r);
    if (mid >= ql) add_sum(ls[k], l, mid, ql, qr);
    if (mid + 1 <= qr) add_sum(rs[k], mid + 1, r, ql, qr);
    pushup(k);
}

void insert(int p, int l, int r, int ql, int qr, ll d)//权值线段树插入
{
    add_sum(rt[p], 1, n, ql, qr);//先更新本结点对应的区间线段树
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (mid >= d) insert((p << 1), l, mid, ql, qr, d);
    else insert((p << 1) | 1, mid + 1, r, ql, qr, d);
}

ll get_sum(int& k, int l, int r, int ql, int qr)//区间线段树查询
{
    if (!k) k = ++tot;
    if (l >= ql && r <= qr)
    {
        return sum[k];
    }
    int mid = (l + r) >> 1;
    pushdown(k, l, r);
    ll kl = 0;
    if (mid >= ql) kl += get_sum(ls[k], l, mid, ql, qr);
    if (mid + 1 <= qr) kl += get_sum(rs[k], mid + 1, r, ql, qr);
    //pushup(k);
    return kl;
}

int kth(int p, int l, int r, int ql, int qr, ll d)//权值线段树查询
{
    if (l == r) return l;
    int mid = (l + r) >> 1;
    ll rcnt = get_sum(rt[p * 2 + 1], 1, n, ql, qr);//右结点对应的值
    if (rcnt >= d) return kth((p << 1) | 1, mid + 1, r, ql, qr, d);
    else return kth((p << 1), l, mid, ql, qr, d - rcnt);
}

void init()//手写unique
{
    int i, j = 1;
    sort(b + 1, b + cnt + 1);
    k[1] = b[1].zh;
    mmp[b[1].zh] = 1;
    for (i = 2; i <= cnt; i++)
    {
        if (b[i].zh == b[j].zh) continue;
        b[++j].zh = b[i].zh;
        k[j] = b[i].zh;
        mmp[b[i].zh] = j;
    }
    cnt = j;
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int i, j;
    scanf("%d%d", &n, &m);
    for (i = 1; i <= m; i++)
    {
        qq[i].id = i;
        scanf("%d%d%d%lld", &qq[i].opt, &qq[i].ql, &qq[i].qr, &qq[i].num);
        if (qq[i].opt == 1)
        {
            b[++cnt].id = i;
            b[cnt].zh = qq[i].num;
        }
    }
    init();
    for (i = 1; i <= m; i++)
    {
        if (qq[i].opt == 1)
        {
            insert(1, 1, cnt, qq[i].ql, qq[i].qr, mmp[qq[i].num]);
        }
        else
        {
            ll ans = k[kth(1, 1, cnt, qq[i].ql, qq[i].qr, qq[i].num)];
            printf("%lld\n", ans);
        }
    }
    return 0;
}

另外,本题显然无法区间线段树套权值线段树,那样的话,每个权值线段树的叶子节点代表[l,r]集合内有多少元素值为k,那样的话无法进行跨区间查询操作,倒是可以查询值为k的元素在[l,r]集合中排第几大。另外,如果不是插入集合,而是单点修改的话,则只需把权值线段树的区间修改改为单点修改即可。

看了两天树套树,感觉实质上和写两颗树代码上区别并不大,但需要时刻弄明白每个变量在干啥。溜去肝嵌入式作业去了~

Gym - 102798G

原题地址

题目大意:一个数组,两种操作,区间+1,和查询两段子数组是否完全一样。

代码:
膜一波队友oi大佬,比赛时1h一发入魂
自己敲的时候想到了用线段树去维护区间hash值,但是一直没想到怎么对65536取模,看了参考博客才明白可以维护一个区间最大值。
非常经典的一道题,感觉敲完后重新找回了对hash和区间线段树的一些理解。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
#define maxn 10000005
#define mkp make_pair
const int inf = 2e9 + 7;
ll base = 998244353;
ll mod = 1e7 + 7;
int n, m;
ll sum[maxn], p[maxn], pre[maxn], a[maxn], ma[maxn], tag[maxn];

//ll gethash(int l, int r)
//{
//    return (str[r] - str[l] * p[r - l + 1] % mod + mod) % mod;
//}

void pushup(int k, int l, int r)
{
    int mid = (l + r) >> 1;
    sum[k] = (sum[k << 1] * p[r - mid] % mod + sum[k << 1 | 1]) % mod;
    ma[k] = max(ma[k << 1], ma[k << 1 | 1]);
}

void pushdown(int k, int l, int r)
{
    if (tag[k])
    {
        int mid = (l + r) >> 1;
        ll tg = tag[k];
        tag[k << 1] = tag[k << 1] + tg;
        tag[k << 1 | 1] = tag[k << 1 | 1] + tg;
        ma[k << 1] = ma[k << 1] + tg;
        ma[k << 1 | 1] = ma[k << 1 | 1] + tg;
        sum[k << 1] = (sum[k << 1] + tg * pre[mid - l] % mod) % mod;
        sum[k << 1 | 1] = (sum[k << 1 | 1] + tg * pre[r - mid - 1] % mod) % mod;
        tag[k] = 0;
    }
}

void build(int k, int l, int r)
{
    //sum[k] = gethash(l, r);
    if (l == r)
    {
        ma[k] = a[l];
        sum[k] = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid);
    build(k << 1 | 1, mid + 1, r);
    pushup(k, l, r);
}

ll query(int k, int l, int r, int ql, int qr)
{
    if (ql <= l && qr >= r)
    {
        //sum[k] = sum[k] + pre[r - l + 1];
        return sum[k] * p[qr - r] % mod;
    }
    int mid = (l + r) >> 1;
    pushdown(k, l, r);
    ll ans = 0;
    if (ql <= mid) ans = ans + query(k << 1, l, mid, ql, qr);
    if (qr >= mid + 1) ans = ans + query(k << 1 | 1, mid + 1, r, ql, qr);
    //pushup(k, l, r);
    ans = ans % mod;
    return ans;
}

void add(int k, int l, int r, int ql, int qr)
{
    if (l == r && ma[k] == 65535)
    {
        ma[k] = 0;
        sum[k] = 0;
        return;
    }
    if (ql <= l && r <= qr && ma[k] < 65535)//敲成了65536,debug了好久
    {
        ma[k]++;
        sum[k] = (sum[k] + pre[r - l]) % mod;
        tag[k]++;
        return;
    }
    int mid = (l + r) >> 1;
    pushdown(k, l, r);
    if (ql <= mid) add(k << 1, l, mid, ql, qr);
    if (qr >= mid + 1) add(k << 1 | 1, mid + 1, r, ql, qr);
    pushup(k, l, r);
}

int main()
{
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int i, j, opt, x, y, l;
    cin >> n >> m;
    p[0] = 1; pre[0] = 1;
    for (i = 1; i <= n; i++)
    {
        p[i] = (p[i - 1] * base) % mod;
        pre[i] = (pre[i - 1] + p[i]) % mod;
    }
    for (i = 1; i <= n; i++)
    {
        cin >> a[i];
        //str[i] = (str[i - 1] * base + a[i]) % mod;
    }
    build(1, 1, n);
    while (m--)
    {
        cin >> opt;
        if (opt == 1)
        {
            cin >> x >> y;
            add(1, 1, n, x, y);
        }
        else
        {
            cin >> x >> y >> l;
            ll xl = query(1, 1, n, x, x + l - 1);
            ll yl = query(1, 1, n, y, y + l - 1);
            if (xl == yl) cout << "yes" << endl;
            else cout << "no" << endl;
        }
    }
    return 0;
}

在add函数中,一度把65535敲成了65536,这样就导致在某些节点的递归会持续下去,于是导致在第8个测试点RE(我还检查了数组大小以及除0错误)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值