这是一个句子测试

1
12
2016
0

BZOJ4364【IOI2014】wall砖墙

Description

健佳正在用大小相同的砖块来砌起一面墙。这面墙由 列砖块所组成,它们从左到右的编号0至n-1。各列的高度可以不同。各列的高度就是该列砖块的数量。健佳用如下方式来建造这面墙。最开始每列都没有砖块。此后,健佳通过k个阶段的增加(adding)或移除(removing)砖块操作来砌墙。当所有k个阶段完成后,这面墙就砌好了。在每个阶段中,健佳都会被告知一个连续的砖块列的范围,以及一个高度值h,然后他就完成如下过程:在增加砖块(adding)阶段,对于给定的列范围中高度小于h的列,健佳会增加砖块使它们的高度都恰好等于h。此时他不会改变那些高度大于或等于h的列。在移除砖块(removing)阶段,对于给定的列范围中高度大于 的列,健佳会移除砖块使它们的高度都恰好等于h。此时他不会改变那些高度小于或等于h的列。你的任务就是计算出这面墙的最后形状。

Input


第1行:n, k。

第2+i 行(0≤i≤k-1):op[i], left[i], right[i], height[i]。

n: 这面墙中的列数。

k: 阶段数。

op: 大小为k的数组;op[i]是第i个阶段的类型:1 表示增加阶段(adding) 而 2表示移除阶段(removing) ,其中0≤i≤k-1。

left 和 right: 大小为k的数组;在第i个阶段中,列的范围从第left[i] 列开始到第right[i]列结束(包括两端 left[i] 和 right[i]),其中0≤i≤k-1。这里保证满足left[i]≤right[i]。

height: 大小为k的数组;height[i] 表示在阶段i的高度参数,其中0≤i≤k-1。

Output

共n行,第i行包含一个整数表示finalHeight[i]。

finalHeight: 大小为n的数组;你需要把第i列砖块的最终数量存放到finalHeight[i]中做为返回结果,其中0≤i≤n-1。

Sample Input

输入样例1
10 3
1 3 4 91220
1 5 9 48623
2 3 5 39412

输入样例2

10 6
1 1 8 4
2 4 9 1
2 3 6 5
1 0 5 3
1 2 2 5
2 6 7 0

Sample Output

输出样例1

0
0
0
39412
39412
39412
48623
48623
48623
48623

输出样例2

3
4
5
4
3
3
0
0
1
0

HINT

 



对于100%的数据,1≤n≤2,000,000,1≤k≤500,000。

【题解】

为什么题目不带BZOJ的格式了?我也不知道。

由于只要维护最值,处理区间时,可以直接对区间最值进行修改。

接着可以用父亲的区间最值去更新儿子的区间最值,然后再用儿子的最值松弛父亲。

接下来是精彩的卡常时间。

首先窝萌阔以手写max和min。

其次窝萌阔以用位运算预处理每个节点的左右儿子。

此外窝萌阔以加inline和读入优化。

然后就水过辣!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=2000010;
int n,m,i,opt,l,r,x;
int mx1[M*4],mx2[M*4];
inline 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;
}
inline void pushdown(int p){
	int l=p<<1,r=p<<1^1;
	if (mx2[p]>mx1[l])mx1[l]=mx2[l]=mx2[p];
		else if (mx2[p]>mx2[l])mx2[l]=mx2[p];
	if (mx2[p]>mx1[r])mx1[r]=mx2[r]=mx2[p];
		else if (mx2[p]>mx2[r])mx2[r]=mx2[p];
	if (mx1[p]<mx2[l])mx1[l]=mx2[l]=mx1[p];
		else if (mx1[p]<mx1[l])mx1[l]=mx1[p];
	if (mx1[p]<mx2[r])mx1[r]=mx2[r]=mx1[p];
		else if (mx1[p]<mx1[r])mx1[r]=mx1[p];
}
inline void pushup(int p){
	int l=p<<1,r=p<<1^1;
	mx1[p]=max(mx1[l],mx1[r]);
	mx2[p]=min(mx2[l],mx2[r]);
}
inline void update1(int x,int y,int l,int r,int p,int t){
	if (x==l&&y==r){
		mx1[p]=max(mx1[p],t);
		mx2[p]=max(mx2[p],t);
		return;
	}
	int mid=l+r>>1;
	pushdown(p);
	if (y<=mid)update1(x,y,l,mid,p<<1,t);
		else if (x>mid)update1(x,y,mid+1,r,p<<1^1,t);
			else update1(x,mid,l,mid,p<<1,t),update1(mid+1,y,mid+1,r,p<<1^1,t);
	pushup(p);
}
inline void update2(int x,int y,int l,int r,int p,int t){
	if (x==l&&y==r){
		mx1[p]=min(mx1[p],t);
		mx2[p]=min(mx2[p],t);
		return;
	}
	int mid=l+r>>1;
	pushdown(p);
	if (y<=mid)update2(x,y,l,mid,p<<1,t);
		else if (x>mid)update2(x,y,mid+1,r,p<<1^1,t);
			else update2(x,mid,l,mid,p<<1,t),update2(mid+1,y,mid+1,r,p<<1^1,t);
	pushup(p);
}
inline void build(int l,int r,int p){
	if (l==r){
		printf("%d\n",mx1[p]);
		return;
	}
	int mid=l+r>>1;
	pushdown(p);
	build(l,mid,p<<1);
	build(mid+1,r,p<<1^1);
}
int main(){
	n=read(),m=read();
	for (i=1;i<=m;i++){
		opt=read();
		if (opt==1){
			l=read(),r=read(),x=read();
			l++;r++;
			update1(l,r,1,n,1,x);
		}else{
			l=read(),r=read(),x=read();
			l++;r++;
			update2(l,r,1,n,1,x);
		}
	}
	build(1,n,1);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
1
5
2016
0

BZOJ2434【NOI2011阿狸的打字机】

Description

 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。

经阿狸研究发现,这个打字机是这样工作的:

l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。

l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a

aa

ab

我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。


阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

Input

 输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数m,表示询问个数。

接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

Output

 输出m行,其中第i行包含一个整数,表示第i个询问的答案。

Sample Input

aPaPBbP
3
1 2
1 3
2 3
 

Sample Output

2
1
0

HINT

 1<=N<=10^5

1<=M<=10^5

输入总长<=10^5

Source

【题解】

isprogrammer炸掉了一个星期真是不爽

做这道题让窝感觉没学过trie图。。。。。

首先要用黑暗的方法构出trie树,即按照读入顺序扫下来,那么有三种情况:

1、碰到字母,新建一个子节点(如果已存在就不用新建),进入该子节点。

2、碰到P,那么打个标记。

3、碰到B,回到父亲节点。

然后求出每个节点的fail,那么询问x在y中出现多少次,只要求root~y中有多少点沿fail能走到x。

那么构出fail树,问题就转化为x的子树中有多少个是root~y中的点。

考虑dfs序,那么求子树和的问题就转化为求片段和,可以用树状数组维护。

对于一个串y,将root~y的点的dfs序插入树状数组中,然后处理所有与y有关的询问就阔以辣!

最后的处理还是可以直接扫描读入的串:

1、碰到字母,将子节点的dfs序加入树状数组,进入该子节点。

2、碰到P,处理询问。

3、碰到B,回到父亲节点。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=100010;
int m,cnt,u,id,i,x,y,edgenum,Edgenum,T;
char s[M];
int pos[M],fa[M],head[M],vet[M],next[M],Head[M],Vet[M],Next[M],tree[2*M],fail[M],h[M],l[M],r[M],ans[M];
int trie[M][26];
void addedge(int x,int y){
    vet[++edgenum]=y;
    next[edgenum]=head[x];
    head[x]=edgenum;
}
void Addedge(int x,int y){
    Vet[++Edgenum]=y;
    Next[Edgenum]=Head[x];
    Head[x]=Edgenum;
}
void update(int x,int y){
    for (int i=x;i<=T;i+=i&-i)tree[i]+=y;
}
int query(int x){
    int ans=0;
    for (int i=x;i;i-=i&-i)ans+=tree[i];
    return ans;
}
void bfs(){
    int l=1,r=1;
    h[l]=1,fail[1]=0;
    while (l<=r){
        int u=h[l];
        for (int i=0;i<26;i++)
            if (trie[u][i]){
                int v=trie[u][i];
                if (u==1)fail[v]=1;else fail[v]=trie[fail[u]][i];
                h[++r]=v;
            }else trie[u][i]=trie[fail[u]][i];
        l++;
    }
}
void dfs(int u){
    l[u]=++T;
    for (int e=head[u];e;e=next[e])dfs(vet[e]);
    r[u]=++T;
}
int main(){
    scanf("%s",s);
    scanf("%d",&m);
    int n=strlen(s);
    cnt=1,u=1,id=0;
    for (i=0;i<26;i++)trie[0][i]=1;
    for (i=0;i<n;i++){
        if (s[i]-'a'>=0){
            if (!trie[u][s[i]-'a'])trie[u][s[i]-'a']=++cnt,fa[cnt]=u;
            u=trie[u][s[i]-'a'];
        }else if (s[i]=='P')pos[++id]=u;else u=fa[u];
    }
    bfs();
    for (i=1;i<=cnt;i++)addedge(fail[i],i);
    dfs(0);
    for (i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        Addedge(y,x);
    }
    u=1,id=0;update(l[1],1);
    for (i=0;i<n;i++){
        if (s[i]-'a'>=0)u=trie[u][s[i]-'a'],update(l[u],1);
            else if (s[i]=='P'){
                id++;
                for (int e=Head[id];e;e=Next[e]){
                    int v=pos[Vet[e]];
                    ans[e]=query(r[v])-query(l[v]-1);
                }   
            }else update(l[u],-1),u=fa[u];
    }
    for (i=1;i<=m;i++)printf("%d\n",ans[i]);
}
12
27
2015
0

BZOJ3295【Cqoi2011】动态逆序对

【题目描述】

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1
样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

 

N<=100000 M<=50000

 

Source

【题解】

终于把要刷的su套su给A掉了。撒花~~~~~~

那么这道题该怎么做呢?

编号为i的数产生的逆序对数是1~i-1中比a[i]大的数的个数和i+1~n中比a[i]小的数的个数。

那么只要先求出整个序列的逆序对数,然后求出每个数产生的逆序对数。

显然发现:有些数已经被删去了,那么就用树状数组套线段su来维护删去的数就阔以辣!

然而窝一开始在直接维护还剩下的点(好写啊),然后被卡内存了。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
const int M=100010;
int n,m,i,x,cnt;
ll ans;
int a[M],pos[M],now[M],now1[M],t[M];
struct node{int l,r;ll w;}tree[M*50];
void insert(int x,int l,int r,int p,int t){
	tree[p].w+=t;
	if (l==r)return;
	int mid=l+r>>1;
	if (x<=mid){
		if (!tree[p].l)tree[p].l=++cnt;
		insert(x,l,mid,tree[p].l,t);
	}else{
		if (!tree[p].r)tree[p].r=++cnt;
		insert(x,mid+1,r,tree[p].r,t);
	}
}
void update(int x,int p,int q){
	for (int i=x;i<=n;i+=i&-i)insert(p,1,n,i,q);
}
ll query(int x,int y,int l,int r,int p){
	if (x==l&&y==r)return tree[p].w;
	int mid=l+r>>1;
	if (y<=mid)return query(x,y,l,mid,tree[p].l);
		else if (x>mid)return query(x,y,mid+1,r,tree[p].r);
			else return query(x,mid,l,mid,tree[p].l)+query(mid+1,y,mid+1,r,tree[p].r);
}
ll Query(int x,int y,int l,int r){
	ll ans=0;
	if (l>r)return 0;
	for (int i=y;i;i-=i&-i)ans+=query(l,r,1,n,i);
	for (int i=x-1;i;i-=i&-i)ans-=query(l,r,1,n,i);
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	cnt=n;
	for (i=1;i<=n;i++){
		scanf("%d",&a[i]);
		pos[a[i]]=i;
		for (int j=a[i];j;j-=j&-j)now[i]+=t[j];
		now[i]=i-1-now[i];
		ans+=now[i];
		for (int j=a[i];j<=n;j+=j&-j)t[j]++;
	}
	memset(t,0,sizeof t);
	for (i=n;i;i--){
		for (int j=a[i];j;j-=j&-j)now1[i]+=t[j];
		for (int j=a[i];j<=n;j+=j&-j)t[j]++;
	}
	for (i=1;i<=m;i++){
		scanf("%d",&x);
		int p=pos[x];
		printf("%lld\n",ans);
		ans-=now[p]+now1[p]-Query(1,p-1,x+1,n)-Query(p+1,n,1,x-1);
		update(p,a[p],1);
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags: 树套树
12
20
2015
0

BZOJ2738矩阵乘法

【题目简介】

Description

 

  给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数。

Input

 
  第一行两个数N,Q,表示矩阵大小和询问组数;
  接下来N行N列一共N*N个数,表示这个矩阵;
  再接下来Q行每行5个数描述一个询问:x1,y1,x2,y2,k表示找到以(x1,y1)为左上角、以(x2,y2)为右下角的子矩形中的第K小数。

Output

  对于每组询问输出第K小的数。

Sample Input

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3
 

Sample Output

1
3
 

HINT

矩阵中数字是109以内的非负整数;

20%的数据:N<=100,Q<=1000;

40%的数据:N<=300,Q<=10000;

60%的数据:N<=400,Q<=30000;

100%的数据:N<=500,Q<=60000。

Source

【题解】

虽然说好要写su套su,但是打隔膜太high结果时间不够了。。。

第一眼看到题目:果奔二维可持久化线段树?(NiroBC确实这么写了)

既然是离线还是用简单快速的乱搞好辣!

整体二分,对于区间[l,r],询问答案为mid,则将[l,mid]之间的点插入二维树状数组。

然后对于一些询问,如果当前矩阵的元素个数>=k那么就加到左边,否则加到右边。

那么就转化出同样的子问题,左边二分[l,mid],右边二分[mid+1,r]。

于是最后的复杂度就变成了O(mlog3n)辣!

(然而窝的程序狂wa怎么回事)

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=60010;
int n,m,cnt,T,i,j;
int tree[505][505],ans[N],id[N],tmp[N],mark[N];
struct shit{int x1,x2,y1,y2,K;}q[60005];
struct fuck{int x,y,w;}a[250005];
bool operator<(fuck a,fuck b){return a.w<b.w;}
void add(int x,int y,int t){
	for(int i=x;i<=n;i+=i&-i)
		for(int j=y;j<=n;j+=j&-j)
			tree[i][j]+=t;
}
int query(int x,int y){
	int tmp=0;
	for(int i=x;i;i-=i&-i)
		for(int j=y;j;j-=j&-j)
			tmp+=tree[i][j];
	return tmp;
}
int query(int k){
	int x1=q[k].x1,y1=q[k].y1,x2=q[k].x2,y2=q[k].y2;
	return query(x2,y2)+query(x1-1,y1-1)-query(x1-1,y2)-query(x2,y1-1);
}
void solve(int l,int r,int L,int R){
	if(l>r||L==R)return;
	int mid=(L+R)>>1;
	while(a[T+1].w<=mid&&T<cnt){add(a[T+1].x,a[T+1].y,1);T++;}
	while(a[T].w>mid){add(a[T].x,a[T].y,-1);T--;}
	int cnt=0;
	for(int i=l;i<=r;i++)
		if(query(id[i])>q[id[i]].K-1){
			mark[i]=1;ans[id[i]]=mid;cnt++;
		}else mark[i]=0;
	int l1=l,l2=l+cnt;
	for(int i=l;i<=r;i++)
		if(mark[i])tmp[l1++]=id[i];
			else tmp[l2++]=id[i];
	for(int i=l;i<=r;i++)id[i]=tmp[i];
	solve(l,l1-1,L,mid);
	solve(l1,l2-1,mid+1,R);
}
int main(){
	scanf("%d%d",&n,&m);
	int mx=0;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++){
			a[++cnt].x=i,a[cnt].y=j,scanf("%d",&a[cnt].w);
			mx=max(a[cnt].w,mx);
		}
	sort(a+1,a+cnt+1);
	for(i=1;i<=m;i++)scanf("%d%d%d%d%d",&q[i].x1,&q[i].y1,&q[i].x2,&q[i].y2,&q[i].K);
	for(i=1;i<=m;i++)id[i]=i;
	solve(1,m,0,mx+1);
	for(i=1;i<=m;i++)printf("%d\n",ans[i]);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
20
2015
0

BZOJ3505【Cqoi2014】数三角形

3505: [Cqoi2014]数三角形

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 851  Solved: 525
[Submit][Status][Discuss]

Description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output


输出一个正整数,为所求三角形数量。

Sample Input


2 2

Sample Output

76

 
数据范围
1<=m,n<=1000

HINT

 

Source

【题解】

等下要刷一道su套su,先刷道水题压压gin。

首先在n*m个点中任取三个。

接着减去三点共线的:同行、同列、斜率不为0的。

对于斜线,枚举两点的坐标差,那么易求这样的点对的个数、两点之间的整点数。

那么就阔以辣!

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
typedef long long ll;
using namespace std;
ll n,m,i,j;
ll gcd(int x,int y){
	if (!(x%y))return y;else return gcd(y,x%y);
}
int main(){
	scanf("%lld%lld",&n,&m);
	n++;m++;
	ll ans=(n*m-2)*(n*m-1)*n*m/6;
	ans-=n*(m-2)*(m-1)*m/6;
	ans-=m*(n-2)*(n-1)*n/6;
	for (i=1;i<n;i++)
		for (j=1;j<m;j++)
			ans-=2*(n-i)*(m-j)*(gcd(i,j)-1);
	printf("%lld\n",ans);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
16
2015
0

BZOJ1251序列终结者

【题目简介】

Description

网上有许多题,就是给定一个序列,要你支持几种操作:A、B、C、D。一看另一道题,又是一个序列 要支持几种操作:D、C、B、A。尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技术含量……这样 我也出一道题,我出这一道的目的是为了让大家以后做这种题目有一个“库”可以依靠,没有什么其他的意思。这道题目 就叫序列终结者吧。 【问题描述】 给定一个长度为N的序列,每个序列的元素是一个整数(废话)。要支持以下三种操作: 1. 将[L,R]这个区间内的所有数加上V。 2. 将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。 3. 求[L,R]这个区间中的最大值。 最开始所有元素都是0。

Input

第一行两个整数N,M。M为操作个数。 以下M行,每行最多四个整数,依次为K,L,R,V。K表示是第几种操作,如果不是第1种操作则K后面只有两个数。

Output

对于每个第3种操作,给出正确的回答。

Sample Input

4 4
1 1 3 2
1 2 4 -1
2 1 3
3 2 4
 

Sample Output

2
【数据范围】
N<=50000,M<=100000。
 

HINT

 

Source

【题解】

对此窝还能说什么呢。。。

由于之前的模板写得过于臭长,于是参考(抄写)了Hzwer的Splay模板。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=100010;
int n,m,i,cnt,root,l,r,x,opt;
int fa[M],son[M][2],tag[M],rev[M],data[M],size[M],mx[M];
void pushup(int p){
	int l=son[p][0],r=son[p][1];
	mx[p]=max(max(mx[l],mx[r]),data[p]);
	size[p]=size[l]+size[r]+1;
}
void pushdown(int p){
	int l=son[p][0],r=son[p][1],t=tag[p];
	if (t){
		tag[p]=0;
		if (l){tag[l]+=t;mx[l]+=t;data[l]+=t;}
		if (r){tag[r]+=t;mx[r]+=t;data[r]+=t;}
	}
	if (rev[p]){
		rev[p]=0;
		rev[l]^=1;rev[r]^=1;
		swap(son[p][0],son[p][1]);
	}
}
void build(int l,int r,int p){
	if(l>r)return;
	if (l==r){
		fa[l]=p;size[l]=1;
		if (l<p)son[p][0]=l;else son[p][1]=l;
		return;
	}
	int mid=l+r>>1;
	build(l,mid-1,mid);
	build(mid+1,r,mid);
	fa[mid]=p;pushup(mid);
	if (mid<p)son[p][0]=mid;else son[p][1]=mid;
}
void rotate(int x,int &k){
	int y=fa[x],z=fa[y],l,r;
	if (son[y][0]==x)l=0;else l=1;r=l^1;
	if (y==k)k=x;
		else {if (son[z][0]==y)son[z][0]=x;else son[z][1]=x;}
	fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
	son[y][l]=son[x][r];son[x][r]=y;
	pushup(y);pushup(x);
}
void splay(int x,int &k){
	while (x!=k){
		int y=fa[x],z=fa[y];
		if (y!=k){
			if (son[y][0]==x^son[z][0]==y)rotate(x,k);
				else rotate(y,k);
		}
		rotate(x,k);
	}
}
int findkth(int k,int rank){
	if (tag[k]||rev[k])pushdown(k);
	int l=son[k][0],r=son[k][1];
	if (size[l]+1==rank)return k;
		else if (size[l]>=rank)return findkth(l,rank);
			else return findkth(r,rank-size[l]-1);
}
void update(int l,int r,int val){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	tag[z]+=val;data[z]+=val;mx[z]+=val;
}
void reverse(int l,int r){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	rev[z]^=1;
}
void query(int l,int r){
	int x=findkth(root,l),y=findkth(root,r+2);
	splay(x,root);splay(y,son[x][1]);
	int z=son[y][0];
	printf("%d\n",mx[z]);
}
int main(){
	mx[0]=-1000000007;
	scanf("%d%d",&n,&m);
	build(1,n+2,0);root=(n+3)>>1;cnt=n+2;
	for (i=1;i<=m;i++){
		scanf("%d",&opt);
		switch(opt){
			case 1:scanf("%d%d%d",&l,&r,&x);update(l,r,x);break;
			case 2:scanf("%d%d",&l,&r);reverse(l,r);break;
			case 3:scanf("%d%d",&l,&r);query(l,r);break;
		}
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
10
2015
0

BZOJ1901Dynamic Rankings

【题目简介】

Description

给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。

Input

对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Output

 

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

HINT

 

20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。

 

Source

【题解】

假如不带修改,那么就是可持久化线段树的模板题。

那么窝萌注意到:可持久化线段树维护的其实是一个片段和一样的东西。

对于片段和的维护,如果不带修改,那么就可以直接用数组,否则就需要树状数组。

那么窝萌是不是可以用树状数组的方式来维护可持久化线段树呢?

显然是可以的,只要把每棵可持久化线段树看作树状数组上的点就可以了。

对于修改,每次都要改logn棵树;查询同样需要logn棵树的值相加。

所以时空复杂度都是nlogn2的。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=20010;
int A[M],a[M],L[100],R[100],op[M][5],cnt,_n,n,m,i;
struct node{int l,r,w;}tree[M*100];
bool cmp(int i,int j){return A[i]<A[j];}
void insert(int x,int l,int r,int p,int t){
	tree[p].w+=t;
	if (l==r)return;
	int mid=l+r>>1;
	if (x<=mid){
		if (!tree[p].l)tree[p].l=++cnt;
		insert(x,l,mid,tree[p].l,t);
	}else{
		if (!tree[p].r)tree[p].r=++cnt;
		insert(x,mid+1,r,tree[p].r,t);
	}
}
void update(int x,int p,int q){
	for (;x<=n;x+=x&-x)insert(p,1,_n,x,q);
}
int query(int l,int r,int k){
	if (l==r)return l;
	int i,x=0,y=0;
	for (i=1;i<=L[0];i++)x+=tree[tree[L[i]].l].w;
	for (i=1;i<=R[0];i++)y+=tree[tree[R[i]].l].w;
	int mid=l+r>>1;
	if (y-x>=k){
		for (i=1;i<=L[0];i++)L[i]=tree[L[i]].l;
		for (i=1;i<=R[0];i++)R[i]=tree[R[i]].l;
		return query(l,mid,k);
	}else{
		for (i=1;i<=L[0];i++)L[i]=tree[L[i]].r;
		for (i=1;i<=R[0];i++)R[i]=tree[R[i]].r;
		return query(mid+1,r,k-y+x);
	}
}
int Query(int l,int r,int k){
	L[0]=0;for (int i=l-1;i;i-=i&-i)L[++L[0]]=i;
	R[0]=0;for (int i=r;i;i-=i&-i)R[++R[0]]=i;
	return query(1,_n,k);
}
int main(){
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)scanf("%d",&a[i]),A[i]=a[i];
	_n=n;
	for (i=1;i<=m;i++){
		char s[5];
		scanf("%s",s);
		if (s[0]=='Q'){
			op[i][0]=0;
			scanf("%d%d%d",&op[i][1],&op[i][2],&op[i][3]);
		}else{
			op[i][0]=1;
			scanf("%d%d",&op[i][1],&op[i][2]);
			A[++_n]=op[i][2];
		}
	}
	sort(A+1,A+_n+1),cnt=n;
	_n=unique(A+1,A+_n+1)-A-1;
	for (i=1;i<=n;i++)a[i]=lower_bound(A+1,A+_n+1,a[i])-A;
	for (i=1;i<=m;i++)if (op[i][0])op[i][2]=lower_bound(A+1,A+_n+1,op[i][2])-A;
	for (i=1;i<=n;i++)update(i,a[i],1);
	for (i=1;i<=m;i++)
		if (op[i][0]){
			update(op[i][1],a[op[i][1]],-1);
			a[op[i][1]]=op[i][2];
			update(op[i][1],a[op[i][1]],1);
		}else printf("%d\n",A[Query(op[i][1],op[i][2],op[i][3])]);
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
4
2015
0

BZOJ3261最大异或和

【题目描述】

Description

     

给定一个非负整数序列 {a},初始长度为 N。       
有   M个操作,有以下两种操作类型:
 
1 、A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 N+1。
2 、Q l r x:询问操作,你需要找到一个位置 p,满足 l<=p<=r,使得:
 
a[p] xor a[p+1] xor ... xor a[N] xor x 最大,输出最大是多少。  

Input

第一行包含两个整数 N  ,M,含义如问题描述所示。   
第二行包含 N个非负整数,表示初始的序列 A 。 
 
接下来 M行,每行描述一个操作,格式如题面所述。   

Output

假设询问操作有 T个,则输出应该有 T行,每行一个整数表示询问的答案。

Sample Input

5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6

Sample Output

4
5
6

HINT

 

对于      100%  的数据,     0<=a[i]<=10^7  

 

【题解】

首先要知道如何求一个数列的最大区间异或和。

令f[i]=a[1] xor a[2] xor …… xor a[i],那么区间[l,r]的异或值就是f[r] xor f[l-1]。

于是我们把f[i]转化为二进制插入线段树,那么枚举r,可以在trie树上贪心地寻找l。

那么这道题只要把f[i]插入可持久化线段树就可以了。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=300010;
int n,m,sum,i,l,r,x,cnt;
int a[M*10],root[M*10];
struct node{int l,r,w;}tree[M*50];
void addtrie(int &p,int x,int dep){
	tree[++cnt]=tree[p],p=cnt,tree[p].w++;
	if (dep<0)return;
	if (x&(1<<dep))addtrie(tree[p].r,x,dep-1);else addtrie(tree[p].l,x,dep-1);
}
int query(int l,int r,int x){
	int ans=0,p=root[l-1],q=root[r];
	for (int i=25;i>=0;i--)
		if (x&(1<<i)){
			if (tree[tree[q].l].w-tree[tree[p].l].w)p=tree[p].l,q=tree[q].l,ans+=1<<i;
				else p=tree[p].r,q=tree[q].r;	
		}else{
			if (tree[tree[q].r].w-tree[tree[p].r].w)p=tree[p].r,q=tree[q].r,ans+=1<<i;
				else p=tree[p].l,q=tree[q].l;
		}
	return ans;
}
int main(){
	scanf("%d%d",&n,&m);
	n++;
	int sum=0;
	addtrie(root[1],sum,25);
	for (i=2;i<=n;i++){
		scanf("%d",&a[i]);
		sum^=a[i];
		root[i]=root[i-1];
		addtrie(root[i],sum,25);
	}
	for (i=1;i<=m;i++){
		char opt[10];
		scanf("%s",opt);
		if (opt[0]=='A'){
			scanf("%d",&x);
			a[++n]=x;
			sum^=a[n];
			root[n]=root[n-1];
			addtrie(root[n],sum,25);	
		}else{
			scanf("%d%d%d",&l,&r,&x);
			printf("%d\n",query(l,r,sum^x));
		}
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
4
2015
0

BZOJ3673/3674可持久化并查集

【题目描述】

Description

Description:
自从zkysb出了可持久化并查集后……
hzwer:乱写能AC,暴力踩标程
KuribohG:我不路径压缩就过了!
ndsf:暴力就可以轻松虐!
zky:……

n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x = x xor lastans,lastans的初始值为0
0<n,m<=2*10^5


 

Input

 

Output

 

Sample Input

5 6
1 1 2
3 1 2
2 1
3 0 3
2 1
3 1 2

Sample Output

1
0
1

HINT

 

Source

【题解】

其实窝萌可以不用路径压缩,只要按秩合并就可以了。

那么我们需要一个可持久化数组来维护每个集合的大小和关键字。

那么用可持久化线段树来实现就可以了。

然而在交加强版的时候无故RE不多说。。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=210000;
struct Node{int l,r,fa,cnt;}tree[N*50];
int root[N],cnt,n,m;
void update(int l,int r,int &p,int x,int y,int z){
	tree[++cnt]=tree[p];p=cnt;
	if(l==r){tree[p].fa=y;tree[p].cnt=z;return;}
	int mid=l+r>>1;
	if(x<=mid)update(l,mid,tree[p].l,x,y,z);else update(mid+1,r,tree[p].r,x,y,z);
}
int query(int l,int r,int p,int x){
	if(l==r)return p;
	int mid=l+r>>1;
	if(x<=mid)return query(l,mid,tree[p].l,x);else return query(mid+1,r,tree[p].r,x);
}
int search(int p,int x){
	int t=query(1,n,p,x);
	if(tree[t].fa==x)return t;else return search(p,tree[t].fa);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)update(1,n,root[0],i,i,1);
	int ans=0;
	for(int i=1;i<=m;i++){
		int op,x,y;
		scanf("%d",&op);
		root[i]=root[i-1];
		if(op==1){
			scanf("%d%d",&x,&y);
			x^=ans;
			y^=ans;
			int p=search(root[i],x),q=search(root[i],y);
			if(tree[p].fa==tree[q].fa)continue;
			if(tree[p].cnt>tree[q].cnt)swap(p,q);
			update(1,n,root[i],tree[p].fa,tree[q].fa,0);
			update(1,n,root[i],tree[q].fa,tree[q].fa,tree[p].cnt+tree[q].cnt);
		}else
		if(op==2){
			scanf("%d",&x);
			x^=ans;
			root[i]=root[x];
		}else{
			scanf("%d%d",&x,&y);
			x^=ans;
			y^=ans;
			int p=search(root[i],x),q=search(root[i],y);
			printf("%d\n",ans=(tree[p].fa==tree[q].fa));
		}
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags:
12
2
2015
0

BZOJ3207花神的嘲讽计划Ⅰ

【题目描述】

Description

背景
花神是神,一大癖好就是嘲讽大J,举例如下:
“哎你傻不傻的!【hqz:大笨J】”
“这道题又被J屎过了!!”
“J这程序怎么跑这么快!J要逆袭了!”
……
描述
这一天DJ在给吾等众蒟蒻讲题,花神在一边做题无聊,就跑到了一边跟吾等众蒟蒻一起听。以下是部分摘录:
1.
“J你在讲什么!”
“我在讲XXX!”
“哎你傻不傻的!这么麻烦,直接XXX再XXX就好了!”
“……”
2.
“J你XXX讲过了没?”
“……”
“那个都不讲你就讲这个了?哎你傻不傻的!”
“……”
DJ对这种情景表示非常无语,每每出现这种情况,DJ都是非常尴尬的。
经过众蒟蒻研究,DJ在讲课之前会有一个长度为N方案,我们可以把它看作一个数列;
同样,花神在听课之前也会有一个嘲讽方案,有M个,每次会在x到y的这段时间开始嘲讽,为了减少题目难度,每次嘲讽方案的长度是一定的,为K。
花神嘲讽DJ让DJ尴尬需要的条件:
在x~y的时间内DJ没有讲到花神的嘲讽方案,即J的讲课方案中的x~y没有花神的嘲讽方案【这样花神会嘲讽J不会所以不讲】。
经过众蒟蒻努力,在一次讲课之前得到了花神嘲讽的各次方案,DJ得知了这个消息以后欣喜不已,DJ想知道花神的每次嘲讽是否会让DJ尴尬【说不出话来】。
 

Input

第1行3个数N,M,K;
第2行N个数,意义如上;
第3行到第3+M-1行,每行K+2个数,前两个数为x,y,然后K个数,意义如上;

Output

对于每一个嘲讽做出一个回答会尴尬输出‘Yes’,否则输出‘No’

Sample Input

8 5 3
1 2 3 4 5 6 7 8
2 5 2 3 4
1 8 3 2 1
5 7 4 5 6
2 5 1 2 3
1 7 3 4 5

Sample Output

No
Yes
Yes
Yes
No

HINT

题中所有数据不超过2*10^9;保证方案序列的每个数字<=N

2~5中有2 3 4的方案,输出No,表示DJ不会尴尬

1~8中没有3 2 1的方案,输出Yes,表示DJ会尴尬

5~7中没有4 5 6的方案,输出Yes,表示DJ会尴尬

2~5中没有1 2 3的方案,输出Yes,表示DJ会尴尬

1~7中有3 4 5的方案,输出No,表示DJ不会尴尬

Source

【题解】

题意真是冗长不清,其实就是:询问一个区间内有没有某个子串。

那么考虑hash。

把连续k个数字存起来,然后扔进可持久化线段树就可以了。

这里有一个技巧:开一个unsigned long long让其自然溢出,当然由于线段树是实时开结点的所以l和r可以搞得很大。

这里有一个trick:mid=l+r>>1的赋值方法会RE,所以要用更精妙的写法。

【Codes】

#include<cstdio>
#include<algorithm>
#include<cstring>
#define inf 18446744073709551615UL
typedef unsigned long long ull;
using namespace std;
const int M=200010;
int n,m,k,i,x,y,cnt;
int root[M],a[M],b[M];
ull hash[M];
struct node{int l;int r;int w;}tree[M*40];
void update(ull l,ull r,int &p,ull tmp){
	tree[++cnt]=tree[p],p=cnt,tree[p].w++;
	if (l==r)return;
	ull mid=l/2+r/2;
	if ((l&1)&&(r&1))mid++;
	if (tmp<=mid)update(l,mid,tree[p].l,tmp);
		else update(mid+1,r,tree[p].r,tmp);
}
int query(ull l,ull r,int p,ull tmp){
	if (l==r)return tree[p].w;
	ull mid=l/2+r/2;
	if ((l&1)&&(r&1))mid++;
	if (tmp<=mid)return query(l,mid,tree[p].l,tmp);
		else return query(mid+1,r,tree[p].r,tmp);
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for (i=1;i<=n;i++)scanf("%d",&a[i]),hash[i]=hash[i-1]*233+a[i];
	ull tmp=1;for (i=1;i<=k;i++)tmp*=233;
	for (i=k;i<=n;i++)root[i]=root[i-1],update(0,inf,root[i],hash[i]-hash[i-k]*tmp);
	while (m--){
		scanf("%d%d",&x,&y);
		for (i=1;i<=k;i++)scanf("%d",&b[i]);
		ull tmp=0;
		for (i=1;i<=k;i++)tmp=tmp*233+b[i];
		if (query(0,inf,root[y],tmp)-query(0,inf,root[x+k-2],tmp))printf("No\n");
			else printf("Yes\n");
	}
}
Category: 蒟蒻的BZOJ刷题记 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com