POJ 3298(用unique离散化优化zkw线段树)

Language:
Antimonotonicity
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 2753   Accepted: 1175

Description

Mary数列是指在一个长度为n的序列(元素大小不超过n)中找到如下的子序列:

Mary0 > Mary1 < Mary2 > Mary3 < ...

请求出它的最长序列大小。

Input

第一行为数据数 T ≤ 50

接下来T行,每行第一个数n( 30000)表示原序列大小,接下来n个数为给定序列

Output

对每组数据输出一行为Mary数列最长长度。

Sample Input

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

Sample Output

1
2
5
3

Source

这题和LIS有异曲同工之妙,都是在给定区间找最大值

显然可以建立两棵线段树(并互相传递值),表示(...?<a[i]) 中使得“..”长度最长的大小,

由于用Unique(指针头,指针尾+1)离散了序列,用-INF和INF表示边界(特别注意离散Hash-map<int,int> Hpos一定要开在Struct外,否则反复建会超时(平衡树用来干这个……)

于是t.t[i][j] 表示第ith线段树的端点值。

i=0表示(1,3,5... 即除1外前面跟了<号的)的数

i=1表示(2,4,6... 即前面跟>的)数


于是本题转化为维护(..?<)的最长长度。

同理建立(..?>),注意特判序列开头那个数(第二个序列的长度必须超过1,表示其并非开头)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
#include<iostream>
#include<map>
using namespace std;
#define MAXN (30000+10)
#define INF (2139062143)
#define NDEBUG
map<int,int> hpos;
struct SegMentTree  //t[0]->'?<' t[1]->'?>'
{
	int n,M,t[2][MAXN*5],a[MAXN],a_sort[MAXN],size;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;while (M-2<n) M<<=1;
		memset(t,0,sizeof(t));
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		memcpy(a_sort,a,sizeof(a));
		sort(a_sort+1,a_sort+n+1);
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		#endif
		size=unique(a_sort+1,a_sort+n+1)-(a_sort+1);
		#ifndef NDEBUG
		for (int i=1;i<=size;i++) cout<<a_sort[i]<<' ';
		cout<<endl;
		cout<<size;
		#endif
		for (int i=1;i<=size;i++) hpos[a_sort[i]]=i;
		hpos[-INF]=0;hpos[INF]=size+1;
	}
	void update(int x,int type)
	{
		for (x>>=1;x;x>>=1) t[type][x]=max(t[type][x<<1],t[type][(x<<1)^1]);
	}
	void insert(int x,int c,int type)
	{
		x=hpos[x]+M;
		if (t[type][x]<c)
		{
			t[type][x]=c;
			update(x,type);
		}
	}
	int find(int l,int r,int type)
	{
		l=hpos[l];r=hpos[r];
//		if (type) l++; else r--;
		#ifndef NDEBUG
		cout<<l<<' '<<r<<';'<<endl;
		#endif

		l+=M;r+=M;
		int ans=0;
		if (l>=r-1) return 0;
		for (;l^r^1;l>>=1,r>>=1)
		{
			if (~l&1) ans=max(ans,t[type][l+1]);
			if (r&1) ans=max(ans,t[type][r-1]);
		}
		return ans;
	}

}t;
int tt,n,ans;
int main()
{
	#ifndef NDEBUG
	freopen("poj3298.in","r",stdin);
	#endif
	scanf("%d",&tt);
	while (tt--)
	{
		ans=0;
		scanf("%d",&n);
		t=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value=t.a[i];
			int value1=t.find(-INF,value,0)+1;
			int value2=t.find(value,INF,1)+1;
			t.insert(value,value1,1);
			ans=max(ans,value1);
			#ifndef NDEBUG
			cout<<"Add?> "<<value<<" f[i]="<<value1<<endl;
			#endif
			if (value2==1) continue;
			t.insert(value,value2,0);
			ans=max(ans,value2);
			#ifndef NDEBUG
			cout<<"Add?< "<<value<<" f[i]="<<value2<<endl;
			#endif
		}
		cout<<ans<<endl;
		#ifndef NDEBUG
		for (int i=1;i<=t.M*2;i++) if (t.t[0][i]) cout<<i<<':'<<t.t[0][i]<<' ';
		cout<<endl;
		#endif
	}

	return 0;
}

其实这题有更Easy的解法……贪心!

如果我们把原序列看成一条直线,且另a[0]和a[n+1]=-INF

那么如图所示


显然当最后的折现向下时:


显然解为凸点数*2

而当最后的折线向上时:



解为凸点数*2-1

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
#define MAXN (30000+10)
int tt,n,a[MAXN];
int main()
{
	scanf("%d",&tt);
	while (tt--)
	{
		scanf("%d",&n);
		int ans=0;
		a[0]=a[n+1]=-0xfffffff;
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		for (int i=1;i<=n;i++) if (a[i-1]<a[i]&&a[i]>a[i+1]) ans++;
		ans*=2;
		if (a[n-1]<a[n]) ans--;
		cout<<ans<<endl;
	}

	return 0;
}



POJ 1631(O(nlogn)LIS的2种做法)

Language:
Bridging signals
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 8574   Accepted: 4635

Description

对于一个二分图的完全匹配,请找出最多的边使其两两不相交。

Input

第一行为测试数据数t,
对于每组数据,第一行为匹配数 p < 40000,
接下来p行,每行1个数a[i],表示左边第i个端点与右边第a[i]个端点相连

Output

对每组数据,输出一行ans,表示最大不相交匹配数

Sample Input

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

Sample Output

3
9
1
4

Source

这题显然可以转化为a[i]的LIS

LIS的一般做法如下:

f[i]表示以i为最后一个元素的最长序列数,

f[i]=f[j]+1(a[j]<a[i],j<i)

nLogn 算法1:

显然上面的方程有1维n是用来求‘小于a[i]且在a[i]前面的,最大的数‘

单从这个定义考虑,

于是问题转化成-维护序列max(f[i]),每一次增加1个点的值,求[1,value_i)的最大值(若无值则为0)

不妨用一个zkw线段树维护(本题max(f[i])的长度=n,若没有这个条件时间会退化到O(nLogT)(T为a[i]的最大值),那么请把原序列排序O(nLogn)+离散化O(n),这样复杂度就有O(nLogT)降至O(nLogn)    ).

程序1如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (40000+10)
#define NDEBUG
int t,n;
struct SegMentTree
{
	int a[MAXN*10],n;
	int M;
	SegMentTree(){}
	SegMentTree(int _n):n(_n)
	{
		M=1;
		while (M-2<n) M<<=1;
		memset(a,0,sizeof(a));
	}
	void insert(int _x,int c)
	{
		_x+=M;
		if (a[_x]<c)
		{
			a[_x]=c;
			for (_x>>=1;_x;_x>>=1) a[_x]=max(a[_x<<1],a[(_x<<1)^1]);
		}
	}
	int find(int l,int r)
	{
		int ans=0;
		l--;r++;
		l+=M;r+=M;
		while (l^r^1)
		{
			if (~l&1) ans=max(ans,a[l+1]);
			if (r&1) ans=max(ans,a[r-1]);
			l>>=1;r>>=1;
		}
		return ans;
	}
}a;
int main()
{
	#ifndef NDEBUG
	freopen("poj1631.in","r",stdin);
	#endif
	scanf("%d",&t);
	while (t--)
	{
		cin>>n;
		a=SegMentTree(n);
		for (int i=1;i<=n;i++)
		{
			int value;
			scanf("%d",&value);
			a.insert(value,a.find(1,value-1)+1);
		}
		printf("%dn",a.find(1,n));

	}
	return 0;
}

算法2:

仔细观察推导序列求最大值部分,发现i总从1开始[1,value_i)

于是可二分查找序列Max[I]'=Max[ F[p] ] (1≤p≤i)

Program LCS;
var
   a,d,f:array[1..100000] of longint;
   n,i,j,len,test:longint;
function search(k:longint):longint;
var
   i,j,m:longint;
begin
   i:=1; j:=len;
   m:=d[(i+j) div 2];
   while (i<=j) do
   begin
      m:=(i+j) div 2;
      if (d[m]<k) and (d[m+1]>=k) then exit(m)
      else if (d[m]<k) then i:=m+1
      else j:=m-1;
   end;
end;
begin
   read(test);
   while (test>0) do
   begin
      read(n);
      len:=1;
      fillchar(d,sizeof(d),0);
      for i:=1 to n do read(a[i]);
      d[1]:=a[1];
      f[1]:=1;
      for i:=2 to n do
      begin
         if (a[i]>d[len]) then
         begin
            inc(len);
            d[len]:=a[i];
            f[i]:=len;
         end
         else if (a[i]<=d[1]) then
         begin
            d[1]:=a[i];
            f[i]:=1;
         end
         else
         begin
            j:=search(a[i]);
            d[j+1]:=a[i];
            f[i]:=j+1;
         end;
      end;
      writeln(len);
      dec(test);
   end;
end.




Tyvj P2068(寻宝)

P2068 - [NOIP2012P2]寻宝

From luchangzhou    Normal (OI)
总时限:10s    内存限制:128MB    代码长度限制:64KB

背景 Background

NOIP 2012 普及组 题2

描述 Description

传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏 宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。说明书的内容如下: 藏宝楼共有 N+1 层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外, 藏宝楼另有 N 层,每层 M 个房间,这 M 个房间围成一圈并按逆时针方向依次编号为 0,…, M-1。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个 指示牌,指示牌上有一个数字 x,表示从这个房间开始按逆时针方向选择第 x 个有楼梯的房 间(假定该房间的编号为 k),从该房间上楼,上楼后到达上一层的 k 号房间。比如当前房 间的指示牌上写着 2,则按逆时针方向开始尝试,找到第 2 个有楼梯的房间,从该房间上楼。 如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。

寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示 牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”。

请帮助小明算出这个打开宝箱的密钥。

输入格式 InputFormat

第一行 2 个整数 N 和 M,之间用一个空格隔开。N 表示除了顶层外藏宝楼共 N 层楼,M 表示除顶层外每层楼有 M 个房间。

       接下来 N*M 行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况, 其中第(i-1)*M+j 行表示第 i 层 j-1 号房间的情况(i=1, 2, …, N;j=1, 2, … ,M)。第一个整数 表示该房间是否有楼梯通往上一层(0 表示没有,1 表示有),第二个整数表示指示牌上的数 字。注意,从 j 号房间的楼梯爬到上一层到达的房间一定也是 j 号房间。

      最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号 从 0 开始)。

输出格式 OutputFormat

输出只有一行,一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对 20123取模的结果即可。

样例输入 SampleInput [复制数据]

2 3
1 2
0 3
1 4
0 1
1 5
1 2
1

样例输出 SampleOutput [复制数据]

5

数据范围和注释 Hint

【输入输出样例说明】

第一层:

0 号房间,有楼梯通往上层,指示牌上的数字是 2;

1 号房间,无楼梯通往上层,指示牌上的数字是 3;

2 号房间,有楼梯通往上层,指示牌上的数字是 4;

第二层:

0 号房间,无楼梯通往上层,指示牌上的数字是 1;

1 号房间,有楼梯通往上层,指示牌上的数字是 5;

2 号房间,有楼梯通往上层,指示牌上的数字是 2;

小明首先进入第一层(底层)的 1 号房间,记下指示牌上的数字为 3,然后从这个房间 开始,沿逆时针方向选择第 3 个有楼梯的房间 2 号房间进入,上楼后到达第二层的 2 号房间, 记下指示牌上的数字为 2,由于当前房间本身有楼梯通向上层,该房间作为第一个有楼梯的 房间。因此,此时沿逆时针方向选择第 2 个有楼梯的房间即为 1 号房间,进入后上楼梯到达 顶层。这时把上述记下的指示牌上的数字加起来,即 3+2=5,所以打开宝箱的密钥就是 5。

【数据范围】

对于 50%数据,有 0<N≤1000,0<x≤10000;

对于 100%数据,有 0<N≤10000,0<M≤100,0<x≤1,000,000;

来源 Source

NOIP 2012

纯粹的模拟题,由于x很大但M很小,可以%tot[i]

但是这么做可能变为0.



#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (10000+10)
#define MAXM (100+10)
#define MAXX (1000000+10)
#define F (20123)
#define NDEBUG
int n,m;
int a[MAXN][MAXM],tot[MAXN]={0};
bool b[MAXN][MAXM]={0};
int main()
{
	#ifndef NDEBUG
	freopen("wealth.in","r",stdin);
	#endif
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		for (int j=0;j<=m-1;j++)
		{
			scanf("%d%d",&b[i][j],&a[i][j]);
			tot[i]+=b[i][j];
		}
	#ifndef NDEBUG
	for (int i=1;i<=n;i++) cout<<tot[i]<<' ';
	#endif
	int x,ans=0;
	scanf("%d",&x);
	for (int i=1;i<=n;i++)
	{
		ans=(ans+a[i][x])%F;
		int len=a[i][x]%tot[i];
		if (len==0) len=tot[i];
		while(len)
		{
			len-=b[i][x];
			if (len==0) break;
			x=(x+1)%m;
		}
	}
//	cout<<x<<endl;
	cout<<ans<<endl;
	return 0;
}

国家集训队论文分类整理

距离ACM/ICPC的时间越来越少了,选择性地看一些集训队论文是很有必要的。

组合数学 计数与统计 2001 - 符文杰:《Pólya原理及其应用》
2003 - 许智磊:《浅谈补集转化思想在统计问题中的应用》
2007 - 周冬:《生成树的计数及其应用》
2008 - 陈瑜希《Pólya计数法的应用》
数位问题 2009 - 高逸涵《数位计数问题解法研究》
2009 - 刘聪《浅谈数位类统计问题》
动态统计 2004 - 薛矛:《解决动态统计问题的两把利刃》
2007 - 余江伟:《如何解决动态统计问题》
博弈 2002 - 张一飞:《由感性认识到理性认识——透析一类搏弈游戏的解答过程》
2007 - 王晓珂:《解析一类组合游戏》
2009 - 曹钦翔《从“k倍动态减法游戏”出发探究一类组合游戏问题》
2009 - 方展鹏《浅谈如何解决不平等博弈问题》
2009 - 贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》
母函数 2009 - 毛杰明《母函数的性质及应用》
拟阵 2007 - 刘雨辰:《对拟阵的初步研究》
线性规划 2007 - 李宇骞:《浅谈信息学竞赛中的线性规划——简洁高效的单纯形法实现与应用》
置换群 2005 - 潘震皓:《置换群快速幂运算研究与探讨》
问答交互 2003 - 高正宇:《答案只有一个——浅谈问答式交互问题》 
猜数问题 2003 - 张宁:《猜数问题的研究:<聪明的学生>一题的推广》
2006 - 龙凡:《一类猜数问题的研究》
数据结构 数据结构 2005 - 何林:《数据关系的简化》
2006 - 朱晨光:《基本数据结构在信息学竞赛中的应用》
2007 - 何森:《浅谈数据的合理组织》
2008 - 曹钦翔《数据结构的提炼与压缩》
结构联合 2001 - 高寒蕊:《从圆桌问题谈数据结构的综合运用》
2005 - 黄刚:《数据结构的联合》
块状链表 2005 - 蒋炎岩:《数据结构的联合——块状链表》
2008 - 苏煜《对块状链表的一点研究》
动态树 2006 - 陈首元:《维护森林连通性——动态树》
2007 - 袁昕颢:《动态树及其应用》
左偏树 2005 - 黄源河:《左偏树的特点及其应用》
跳表 2005 - 魏冉:《让算法的效率“跳起来”!——浅谈“跳跃表”的相关操作及其应用》
2009 - 李骥扬《线段跳表——跳表的一个拓展》
SBT 2007 - 陈启峰:《Size Balance Tree》
线段树 2004 - 林涛:《线段树的应用》
单调队列 2006 - 汤泽:《浅析队列在一类单调性问题中的应用》
哈希表 2005 - 李羽修:《Hash函数的设计优化》
2007 - 杨弋:《Hash在信息学竞赛中的一类应用》
Splay 2004 - 杨思雨:《伸展树的基本操作与应用》
图论 图论 2005 - 任恺:《图论的基本思想及方法》
模型建立 2004 - 黄源河:《浅谈图论模型的建立与应用》
2004 - 肖天:《“分层图思想”及其在信息学竞赛中的应用》
网络流 2001 - 江鹏:《从一道题目的解法试谈网络流的构造与算法》
2002 - 金恺:《浅谈网络流算法的应用》
2007 - 胡伯涛:《最小割模型在信息学竞赛中的应用》
2007 - 王欣上:《浅谈基于分层思想的网络流算法》
2008 - 周冬《两极相通——浅析最大—最小定理在信息学竞赛中的应用》
最短路 2006 - 余远铭:《最短路算法及其应用》
2008 - 吕子鉷《浅谈最短径路问题中的分层思想》
2009 - 姜碧野《SPFA算法的优化及应用》
欧拉路 2007 - 仇荣琦:《欧拉回路性质与应用探究》
差分约束系统 2006 - 冯威:《数与图的完美结合——浅析差分约束系统》
平面图 2003 - 刘才良:《平面图在信息学中的应用》
2007 - 古楠:《平面嵌入》
2-SAT 2003 - 伍昱:《由对称性解2-SAT问题》
最小生成树 2004 - 吴景岳:《最小生成树算法及其应用》
2004 - 汪汀:《最小生成树问题的拓展》
二分图 2005 - 王俊:《浅析二分图匹配在信息学竞赛中的应用》
Voronoi图 2006 - 王栋:《浅析平面Voronoi图的构造及应用》
偶图 2002 - 孙方成:《偶图的算法及应用》
2002 - 周文超:《树结构在程序设计中的运用》
2005 - 栗师:《树的乐园——一些与树有关的题目》
路径问题 2009 - 漆子超《分治算法在树的路径问题中的应用》
最近公共祖先 2007 - 郭华阳:《RMQ与LCA问题》
划分问题 2004 - 贝小辉:《浅析树的划分问题》
数论 欧几里得算法 2009 - 金斌《欧几里得算法的应用》
同余方程 2003 - 姜尚仆:《模线性方程的应用——用数论方法解决整数问题》
搜索 搜索 2001 - 骆骥:《由“汽车问题”浅谈深度搜索的一个方面——搜索对象与策略的重要性》
2002 - 王知昆:《搜索顺序的选择》
2005 - 汪汀:《参数搜索的应用》
启发式 2009 - 周而进《浅谈估价函数在信息学竞赛中的应用》
优化 2003 - 金恺:《探寻深度优先搜索中的优化技巧——从正方形剖分问题谈起》
2003 - 刘一鸣:《一类搜索的优化思想——数据有序化》
2006 - 黄晓愉:《深度优先搜索问题的优化技巧》
背包问题 2009 - 徐持衡《浅谈几类背包题》
匹配 2004 - 楼天城:《匹配算法在搜索问题中的巧用》
概率 概率 2009 - 梅诗珂《信息学竞赛中概率问题求解初探》
数学期望 2009 - 汤可因《浅析竞赛中一类数学期望问题的解决方法》
字符串 字符串 2003 - 周源:《浅析“最小表示法”思想在字符串循环同构问题中的应用》
多串匹配 2004 - 朱泽园:《多串匹配算法及其启示》
2006 - 王赟:《Trie图的构建、活用与改进》
2009 - 董华星《浅析字母树在信息学竞赛中的应用》
后缀数组 2004 - 许智磊:《后缀数组》
2009 - 罗穗骞《后缀数组——处理字符串的有力工具》
字符串匹配 2003 - 饶向荣:《病毒的DNA———剖析一道字符匹配问题解析过程》
2003 - 林希德:《求最大重复子串》
动态规划 动态规划 2001 - 俞玮:《基本动态规划问题的扩展》
2006 - 黄劲松:《贪婪的动态规划》
2009 - 徐源盛《对一类动态规划问题的研究》
状态压缩 2008 - 陈丹琦《基于连通性状态压缩的动态规划问题》
状态设计 2008 - 刘弈《浅谈信息学中状态的合理设计与应用》
树形DP 2007 - 陈瑜希:《多角度思考创造性思维——运用树型动态规划解题的思路和方法探析》
优化 2001 - 毛子青:《动态规划算法的优化技巧》
2003 - 项荣璟:《充分利用问题性质——例析动态规划的“个性化”优化》
2004 - 朱晨光:《优化,再优化!——从《鹰蛋》一题浅析对动态规划算法的优化》
2007 - 杨哲:《凸完全单调性的加强与应用》
计算几何 立体几何 2003 - 陆可昱:《长方体体积并》
2008 - 高亦陶《从立体几何问题看降低编程复杂度》
计算几何思想 2004 - 金恺:《极限法——解决几何最优化问题的捷径》
2008 - 程芃祺《计算几何中的二分思想》
2008 - 顾研《浅谈随机化思想在几何问题中的应用》
2007 - 高逸涵:《与圆有关的离散化》
半平面交 2002 - 李澎煦:《半平面交的算法及其应用》
2006 - 朱泽园:《半平面交的新算法及其实用价值》
矩阵 矩阵 2008 - 俞华程《矩阵乘法在信息学中的应用》
高斯消元 2002 - 何江舟:《用高斯消元法解线性方程组》
数学方法 数学思想 2002 - 何林:《猜想及其应用》
2003 - 邵烜程:《数学思想助你一臂之力》
数学归纳法 2009 - 张昆玮《数学归纳法与解题之道》
多项式 2002 - 张家琳:《多项式乘法》
数形结合 2004 - 周源:《浅谈数形结合思想在信息学竞赛中的应用》
黄金分割 2005 - 杨思雨:《美,无处不在——浅谈“黄金分割”和信息学的联系》
其他算法 遗传算法 2002 - 张宁:《遗传算法的特点及其应用》
2005 - 钱自强:《关于遗传算法应用的分析与研究》
信息论 2003 - 侯启明:《信息论在信息学竞赛中的简单应用》
染色与构造 2002 - 杨旻旻:《构造法——解题的最短路径》
2003 - 方奇:《染色法和构造法在棋盘上的应用》
一类问题 区间 2008 - 周小博《浅谈信息学竞赛中的区间问题》
2005 - 龙凡:《序的应用》
2006 - 汪晔:《信息学中的参考系与坐标系》
物理问题 2008 - 方戈《浅析信息学竞赛中一类与物理有关的问题》
编码与译码 2008 - 周梦宇《码之道—浅谈信息学竞赛中的编码与译码问题》
对策问题 2002 - 骆骥:《浅析解“对策问题”的两种思路》
优化 算法优化 2002 - 孙林春:《让我们做得更好——从解法谈程序优化》
2004 - 胡伟栋:《减少冗余与算法优化》
2005 - 杨弋:《从<小H的小屋>的解法谈算法的优化》
2006 - 贾由:《由图论算法浅析算法优化》
程序优化 2006 - 周以苏:《论反汇编在时间常数优化中的应用》
2009 - 骆可强《论程序底层优化的一些方法与技巧》
语言 C++ 2004 - 韩文弢:《论C++语言在信息学竞赛中的应用》
策略 策略 2004 - 李锐喆:《细节——不可忽视的要素》
2005 - 朱泽园:《回到起点——一种突破性思维》
2006 - 陈启峰:《“约制、放宽”方法在解题中的应用》
2006 - 李天翼:《从特殊情况考虑》
2007 - 陈雪:《问题中的变与不变》
2008 - 肖汉骏《例谈信息学竞赛分析中的“深”与“广”》
倍增 2005 - 朱晨光:《浅析倍增思想在信息学竞赛中的应用》
二分 2002 - 李睿:《二分法与统计问题》
2002 - 许智磊:《二分,再二分!——从Mobiles(IOI2001)一题看多重二分》
2005 - 杨俊:《二分策略在信息学竞赛中的应用》
调整 2006 - 唐文斌:《“调整”思想在信息学中的应用》
随机化 2007 - 刘家骅:《浅谈随机化在信息学竞赛中的应用》
非完美算法 2005 - 胡伟栋:《浅析非完美算法在信息学竞赛中的应用》
2008 - 任一恒《非完美算法初探》
提交答案题 2003 - 雷环中:《结果提交类问题》
守恒思想 2004 - 何林:《信息学中守恒法的应用》
极限法 2003 - 王知昆:《浅谈用极大化思想解决最大子矩形问题》
贪心 2008 - 高逸涵《部分贪心思想在信息学竞赛中的应用》
压缩法 2005 - 周源:《压去冗余缩得精华——浅谈信息学竞赛中的“压缩法”》
逆向思维 2005 - 唐文斌:《正难则反——浅谈逆向思维在解题中的应用》
穷举 2004 - 鬲融:《浅谈特殊穷举思想的应用》
目标转换 2002 - 戴德承:《退一步海阔天空——“目标转化思想”的若干应用》
2004 - 栗师:《转化目标在解题中的应用》
类比 2006 - 周戈林:《浅谈类比思想》
分割与合并 2006 - 俞鑫:《棋盘中的棋盘——浅谈棋盘的分割思想》
2007 - 杨沐:《浅析信息学中的“分”与“合”》
平衡思想 2008 - 郑暾《平衡规划——浅析一类平衡思想的应用》

CF 1A(隐式转换)

A. Theatre Square
time limit per test

2 seconds

memory limit per test

64 megabytes

input

standard input

output

standard output

在n*m的广场上平铺边长为a的正方形石板,问至少要多少石板才能铺满广场(石板必须对齐且与边角线平行)

Input

输入仅一行 n,  m and a (1 ≤  n, m, a ≤ 109).

Output

输出一行最小石板数。

Sample test(s)
input
6 6 4
output
4

显然答案为(ceil(double(n)/double(a))*ceil(double(m)/double(a)))。

但直接用cout输出可能被转换成科学表示法

且极端情况Maxn*Maxm>2^31

所以输出时要打上(long long)

而且必须框上double()否则中间的‘/'会被当成整除


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
#define MAXN (1000000000)
long long n,m,a;
int main()
{
	cin>>n>>m>>a;
	cout<<(long long)(ceil(double(n)/double(a))*ceil(double(m)/double(a)))<<endl;
	return 0;
}

Tyvj P1180(情况少分支多的过程-Dp)

P1180 - 矿工配餐

From Admin    Normal (OI)
总时限:16s    内存限制:128MB    代码长度限制:64KB

描述 Description

现有两个煤矿,每个煤矿都雇用一组矿工。采煤工作很辛苦,所以矿工们需要良好饮食。每当一辆食品车到达煤矿时,矿工们便会产出一定数量的煤。有三种类型的食品车:肉车,鱼车和面包车。
矿工们喜欢变化的食谱。如果提供的食品能够不断变化,他们的产煤量将会增加。每当一个新的食品车到达煤矿时,矿工们就会比较这种新的食品和前两次(或者少于两次,如果前面运送食品的次数不足两次)的食品,并且:
如果这几次食品车都是同一类型的食品,则矿工们产出一个单位的煤。
如果这几次食品车中有两种不同类型的食品,则矿工们产出两个单位的煤。
如果这几次食品车中有三种不同类型的食品,则矿工们产出三个单位的煤。
预先已知食品车的类型及其被配送的顺序。通过确定哪车食品送到哪个煤矿可以影响产煤量。食品车不能被拆分,每个食品车必须被全部送到一个或另一个煤矿。两个煤矿也并不要求接收相同数量的食品车(事实上,也允许将所有食品车都送到一个煤矿)。
任务
给出食品车的类型及其被配送的顺序,要求你写一个程序,确定哪个食品车应被送到煤矿1,哪个食品车应被送到煤矿2,以使得两个煤矿的产煤量的总和最大。

输入格式 InputFormat

输入的第一行包含一个整数N (1 ≤ N ≤ 100 000),  表示食品车的数目。
第二行包含一个由N个字符组成的字符串,按照配送顺序依次表示食品车配送的食品的类型。每个字符是以下三个大写字母之一:'M' (表示肉类), 'F' (表示鱼类) 或 'B' (表示面包)。

输出格式 OutputFormat

输出一个整数,表示最大的总产煤量。

样例输入 SampleInput [复制数据]

样例输入1
6
MBMFFB

样例输入2
16
MMBMBBBBMMMMMBMB

样例输出 SampleOutput [复制数据]

样例输出1
12

样例输入2
29

数据范围和注释 Hint

在样例1中,可以按照如下的顺序运送食品车:煤矿 1, 煤矿 1, 煤矿 2, 煤矿 2, 煤矿 1, 煤矿 2, 依次产生的产煤量为1, 2, 1, 2, 3 和 3 个单位,一共是12 个单位。还有其它运送方式也能产生上述最大总和的产煤量。

时间限制 TimeLimitation

前10点时限1s,分值8分
后2点时限3s,分值10分

这题看着就是不可解的。

但是仔细观察会发现它的情况较少,分支巨多(2^N)??

于是我们用滚动数组+Dp显然可以用F[i][j][k][l][m]表示子结构

j,k,l,m为2个矿工最近2次的伙食(0表示没有)

任何坑爹的题目先想想能不能Dp……

记忆化搜索不能滚动还会爆栈,于是Dp的存在性证毕。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define NDEBUG
int n,a[MAXN];
int f[2][4][4][4][4]; //1->M 2->F 3->B
int cost[4][4][4]={0};
int _cost(int i,int j,int k)
{
	if (k==0) return 0;
	if (i==0) i=k;
	if (j==0) j=k;
	if (i!=j&&i!=k&&j!=k) return 3;
	if (i!=j||i!=k||j!=k) return 2;
	return 1;
}
int main()
{
	#ifndef NDEBUG
	freopen("Vijos1386.in","r",stdin);
	#endif
	scanf("%d",&n);getchar();
	for (int i=0;i<n;i++)
	{
		switch (getchar())
		{
		case 'M':a[i]=1;break;
		case 'F':a[i]=2;break;
		case 'B':a[i]=3;break;
		}
	}
	for (int i=0;i<=3;i++)
		for (int j=0;j<=3;j++)
			for (int k=0;k<=3;k++)
			{
				cost[i][j][k]=_cost(i,j,k);
				#ifndef NDEBUG
				cout<<i<<' '<<j<<' '<<k<<':'<<cost[i][j][k]<<endl;
				#endif
			}


	memset(f,128,sizeof(f));
	f[0][0][0][0][0]=0;
	for (int ii=0;ii<n;ii++)
	{
		int i=ii%2;
		for (int j=0;j<=3;j++)
			for (int k=0;k<=3;k++)
				for (int l=0;l<=3;l++)
					for (int m=0;m<=3;m++)
					{
						if (f[i][j][k][l][m]>=0)
						{
							f[i^1][k][a[ii]][l][m]=max(f[i^1][k][a[ii]][l][m],f[i][j][k][l][m]+cost[j][k][a[ii]]);
							f[i^1][j][k][m][a[ii]]=max(f[i^1][j][k][m][a[ii]],f[i][j][k][l][m]+cost[l][m][a[ii]]);
						}
					}
	}
	int ii=n%2,ans=0;
	for (int j=0;j<=3;j++)
		for (int k=0;k<=3;k++)
			for (int l=0;l<=3;l++)
				for (int m=0;m<=3;m++)
					ans=max(ans,f[ii][j][k][l][m]);
	cout<<ans<<endl;


	return 0;
}


Tyvj P2065(区间嵌套与统计)

P2065 - 「Poetize10」封印一击

From lydliyudong    Normal (OI)
总时限:10s    内存限制:128MB    代码长度限制:64KB

背景 Background

“圣主applepi于公元2011年9月创造了Nescafe,它在散发了16次光辉之后与公元2011年11月12日被封印为一颗魂珠,贮藏于Nescafe神塔之中。公元2012年9月,圣主带领四大护法重启了Nescafe,如今已经是Nescafe之魂的第30次传播了。不久,它就要被第二次封印,而变成一座神杯……”applepi思索着Nescafe的历史,准备着第二次封印。

描述 Description

Nescafe由n种元素组成(编号为1~n),第i种元素有一个封印区间[ai,bi]。当封印力度E小于ai时,该元素将获得ai的封印能量;当封印力度E在ai到bi之间时,该元素将获得E的封印能量;而当封印力度E大于bi时,该元素将被破坏从而不能获得任何封印能量。现在圣主applepi想选择恰当的E,使得封印获得的总能量尽可能高。为了封印的最后一击尽量完美,就请你写个程序帮他计算一下吧!

输入格式 InputFormat

第一行一个整数N。
接下来N行每行两个整数ai、bi,第i+1行表示第i种元素的封印区间。

输出格式 OutputFormat

两个用空格隔开的整数,第一个数是能够获得最多总能量的封印力度E,第二个数是获得的总能量大小。当存在多个E能够获得最多总能量时,输出最小的E。

样例输入 SampleInput [复制数据]

2
5 10
20 25

样例输出 SampleOutput [复制数据]

10 30

数据范围和注释 Hint

对于 50% 的数据,1<=N<=1000,1<=ai<=bi<=10000。 
对于 100% 的数据,1<=N<=10^5,1<=ai<=bi<=10^9。

时间限制 TimeLimitation

各个测试点1s

区间选取

用Past和In_s维护经过的左右结点,

并由此算出

嵌套数(左-右)

左边的区间数=右

右边的区间数=总-(左-右)-右=总-左

PS:由于是闭区间,需要把-右的时间后延


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (200000+10)
#define MAXAi (1000000000)
#define NDEBUG
struct segment_node
{
	int l,type;
	friend bool operator<(const segment_node a,const segment_node b){return (a.l!=b.l)?a.l<b.l:a.type<b.type;	}
	friend bool operator==(const segment_node a,const segment_node b){return (a.l==b.l&&a.type==b.type);	}
	friend bool operator!=(const segment_node a,const segment_node b){return (!(a==b));	}

}a[MAXN];
int n;
long long next[MAXN];
int main()
{
	#ifndef NDEBUG
	freopen("segmentbet.in","r",stdin);
	#endif
	memset(a,0,sizeof(a));
	scanf("%d",&n);n<<=1;
	for (int i=1;i<=n;i+=2)
	{
		scanf("%d%d",&a[i].l,&a[i+1].l);
		a[i].type=-1;a[i+1].type=1;
	}
	sort(a+1,a+n+1);
	int in_s=0,minE=0;
	long long ans=0;
	memset(next,0,sizeof(next));
	for (int i=n-1;i>=1;i--)
	{
		next[i]=next[i+1];
		if (a[i+1].type==-1)
			next[i]+=a[i+1].l;
	}
	int j=1,past=0,pastE=0;
	for (int i=1;i<=n;i++)
	{
		if (a[i]!=a[i+1])
		{
			int len=i-j+1;
			j=i+1;
			len*=-a[i].type;
			if (pastE!=a[i].l) {in_s+=past;past=0;}
			if (len>0) in_s+=len;
			else past+=len;

			long long cost=(long long)(long long)in_s*(long long)a[i].l+next[i];
			if (ans<cost)
			{
				ans=cost;
				minE=a[i].l;
			}

		}

	}
	cout<<minE<<' '<<ans<<endl;
	return 0;
}



Tyvj P2067(质因数分解)

P2067 - [NOIP2012P1]质因数分解

From luchangzhou    Normal (OI)
总时限:10s    内存限制:128MB    代码长度限制:64KB

背景 Background

NOIP2012

描述 Description

已知正整数n 是两个不同的质数的乘积,试求出较大的那个质数。

输入格式 InputFormat

 输入只有一行,包含一个正整数n 。

输出格式 OutputFormat

 输出只有一行,包含一个正整数p ,即较大的那个质数。 

样例输入 SampleInput [复制数据]

21

样例输出 SampleOutput [复制数据]

7

数据范围和注释 Hint

【数据范围】 
对于 60% 的数据 6 ≤ n ≤ 1000
对于 100%的数据 6 ≤ n ≤ 2*10^9

来源 Source

NOIP2012

O(√n)
注意要枚举1-√n而不是√n-n,数量级差很多。


#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define MAXAi (1000000000+10)
int n;
int main()
{
	cin>>n;
	for (int i=2;i<=n;i++)
		if (!(n%i))
		{
			cout<<n/i<<endl;return 0;
		}


}

CF 242E(zkw线段树-拆位)

E. XOR on Segment
time limit per test

4 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

对于数组,a1, a2, ..., an.
请维护2个操作。

  1. 区间[l, r], 求和.
  2. 将区间 [l, r],上的数异或x
Input

第一行为数组大小 n (1 ≤ n ≤ 105), 第二行为数组 a1, a2, ..., an(0 ≤ ai ≤ 106

第三行为操作数 m (1 ≤ m ≤ 5·104),接下来每行第1个数ti (1 ≤ ti ≤ 2)
表示进行第i种操作. ‘1
li, ri
 ‘(1 ≤ li ≤ ri ≤ n)表示[l,r]求和.‘2 li, ri, xi (1 ≤ li ≤ ri ≤ n, 1 ≤ xi ≤ 106)表示将区间 [l, r],上的数异或x

Output

输出所有操作1的结果。

不要用%lld 占位符,改用输入输出流或%I64占位符.

Sample test(s)
input
5
4 10 3 13 7
8
1 2 4
2 1 3 3
1 2 4
1 3 3
2 2 5 5
1 1 5
2 1 2 10
1 2 3
output
26
22
0
34
11
input
6
4 7 4 0 7 3
5
2 2 3 8
1 1 5
2 3 5 1
2 4 5 6
1 2 3
output
38
28

这题是xor拆位法的运用

显然我们可以用t[i,j]表示第i位上的1的个数,之后求和统计(这是xor操作优化的常用手段)

zkw线段树加Lazy标记时,用tot[]表示区间上的总数(其实也可以时事算出来)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<iostream>
using namespace std;
#define MAXN (100000+10)
#define MAXAi (1000000+10)
#define LogMAXAi (20)
#define MAXm (50000)
#define NDEBUG
int n,m,M,t[LogMAXAi+10][MAXN*10],tot[MAXN*10]={0};
bool b[LogMAXAi+10][MAXN*10]={0};
void build_tree()
{
	for (int i=M+1;i<=M+n;i++) tot[i]=1;
	for (int i=M-1;i>=1;i--) tot[i]=tot[i<<1]+tot[(i<<1)^1];

}
long long count_a_tree_node(int x)
{
	long long ans=0;
	for (int i=0,p=1;i<20;i++,p<<=1) ans+=(long long)(p)*(long long)(t[i][x]);
	return ans;
}
long long quere(int l,int r)
{
	long long ans=0;
	l--;r++;
	l+=M;r+=M;
	while (l^r^1)
	{
		if (~l&1) ans+=count_a_tree_node(l+1);
		if (r&1) ans+=count_a_tree_node(r-1);
		l>>=1;r>>=1;
	}
	return ans;
}
void pushdown(int x)
{
	if (x!=1) pushdown(x>>1);
	for (int i=0;i<20;i++)
	{
		if (b[i][x])
		{
			t[i][x<<1]=tot[x<<1]-t[i][x<<1];
			t[i][(x<<1)^1]=tot[(x<<1)^1]-t[i][(x<<1)^1];
			b[i][x]=0;b[i][x<<1]^=1;b[i][(x<<1)^1]^=1;
		}
	}
}
void update(int x)
{
	for (int i=0;i<20;i++)
	{
		int xx=x;
		for (x>>=1;x;x>>=1)
			t[i][x]=t[i][x<<1]+t[i][(x<<1)^1];
		x=xx;
	}
}
void a_t_xor(int j,int l,int r)
{
	l--;r++;
	l+=M;r+=M;
	while (l^r^1)
	{
		if (~l&1) {t[j][l+1]=tot[l+1]-t[j][l+1];b[j][l+1]^=1;	}
		if (r&1) {t[j][r-1]=tot[r-1]-t[j][r-1];b[j][r-1]^=1;	}
		l>>=1;r>>=1;
	}
}
void t_xor(int l,int r,int x)
{
	for (int i=0;x;i++,x>>=1) {if (x&1) a_t_xor(i,l,r);	}
}
int main()
{
//	freopen("CF_242E.in","r",stdin);
	memset(t,0,sizeof(t));
	scanf("%d",&n);
	M=1;while (M-2<n) M<<=1; //cout<<M<<endl;
	for (int i=M+1;i<=M+n;i++)
	{
		scanf("%d",&t[0][i]);
		int j=0;
		while (t[j][i]) {t[j+1][i]=t[j][i]/2;t[j][i]&=1;j++;}
	}
	for (int j=0;j<=19;j++)
	{
		for (int i=M-1;i>=1;i--) t[j][i]=t[j][i<<1]+t[j][(i<<1)^1];
	}
	#ifndef NDEBUG
	for (int i=1;i<=M+n;i++)
	{
		for (int j=0;j<=19;j++) cout<<t[j][i]<<' ';
		cout<<endl;
	}
	#endif
	build_tree();
	/*
	for (int i=1;i<=M+n;i++) cout<<tot[i]<<' ';
	cout<<endl;
	*/
	scanf("%d",&m);
	while (m)
	{
		int p;
		scanf("%d",&p);
		if (p==1)
		{
			int l,r;scanf("%d%d",&l,&r);
			pushdown(l-1+M);pushdown(r+1+M);
			cout<<quere(l,r)<<endl;
		}
		else
		{
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			pushdown(l-1+M);pushdown(r+1+M);
			t_xor(l,r,x);
			update(l-1+M);update(r+1+M);
		}

		#ifndef	NDEBUG
		for (int i=1;i<=M+n;i++)
		{
			for (int j=0;j<=19;j++) cout<<t[j][i]<<' ';
			cout<<endl;
		}
		#endif
		m--;
	}
	return 0;
}

RQNOJ 60(zkw线段树-xor区间与区间求和)

查看题目 Show Problem

题目:美丽的中国结

问题编号:60


题目描述

题目背景
kitty刚刚高三毕业.看到同学们都回家的回家,旅游的旅游,她的心里有些落寞.英俊潇洒风流倜傥迷倒万千KL却仅对kitty感冒的fish看在眼里,急在心里.一天,fish提出和kitty两个人一起外出旅游.kitty犹豫了几天,想好能瞒过家长的理由后(要问是什么……自己猜去),答应了.fish很高兴地带着kitty去登记了(别想歪,登记旅游团而已……),日照青岛五日游.
当然啦,他们玩得很高兴.虽然这次旅行是fish先提议的,但kitty因为玩得很畅快(刚高考完嘛),所以想送给fish一份礼物,一份能让见多识广的fish都无法忘怀的礼物.她从路边 9¾站台的某算命先生那里得知中国结具有增加RP的效果,而这正是fish所需要的,因此她决定动手给fish编一个奇特的中国结.

题目描述
中国结形式多样,fish会喜欢什么样的呢?思考几天后,kitty决定给fish编一个树状的中国结.这个中国结有n个结点(编号1,2,…,n),这n个结点之间总共恰有n-1条线相连,其中结点1上是树的根.这是一个奇特的中国结,因此它的编织方式也很奇特.在编织过程中的每一步骤,kitty有时需要将一个结点子树里的所有结点i的状态全部取反(如果原来结点i已经打结,则解开i,否则将结点i打结),有时又需要知道一个结点的子树里有多少已经打结的结点,你能帮助可爱的kitty完成这份礼物吗?

数据规模
对于40% 的数据,1≤n≤10000, 1≤m≤20000
对于100%的数据,1≤n≤100000, 1≤m≤100000

输入格式

输入
第一行有个整数n,表示这个中国结有n个结点
以下n-1行,每行有两个整数u和v(1≤u,v≤n),表示结点u和v间有一条线相连;
再一行有个整数m,表示kitty要进行的步骤数
以下m行,每行可能为:
"C x":表示将结点x的子树中所有结点的状态取反(包括x)

"Q x":表示kitty想知道结点x的子树中有多少已经打结的结点(包括x)

输出格式

对于每个“Q x”输出一行整数,表示结点x的子树中有多少已经打结的结点(包括x)

样例输入

5
1 2
1 3
2 4
2 5
5
Q 1
C 1
Q 1
C 2
Q 1

样例输出

0
5
2

zkw线段树在某个区间上取反(xor 1) ,求某个区间的和

zkw线段树的标记永久化在该题中不适用、

所以可以记一般的Lazy标记--

在处理区间时,把这个区间可能用到的点的标记全部下传

处理完区间后update左、右结点,把区间上传(这样就能消除标记上方结点为0对结果的影响)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define MAXm (100000+10)
#define NDEBUG
int n,m,M,t[MAXN*10]={0},l[MAXN],r[MAXN];
bool mark[MAXN*10]={false};
char s[50];
int head[MAXN*2]={0},next[MAXN*2],edge[MAXN*2],size=0;
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=head[u];
	head[u]=size;
}
int tim=0;
bool b[MAXN]={0};
void dfs(int u)
{
	b[u]=1;l[u]=++tim;
	int p=head[u];
	while (p)
	{
		if (!b[edge[p]]) dfs(edge[p]);
		p=next[p];
	}
	r[u]=++tim;
}
void pushdown(int x,int p)
{
	if (!x) return;
	pushdown(x>>1,p<<1);
	if (mark[x>>1])
	{
		mark[x]^=1;mark[x^1]^=1;mark[x>>1]=0;
		t[x]=p-t[x];t[x^1]=p-t[x^1];
	}
	x>>=1;

}
void update(int x)
{
	for (x>>=1;x;x>>=1) t[x]=t[x<<1]+t[(x<<1)^1];
}
int main()
{
	#ifndef NDEBUG
	freopen("RQNOJ60.in","r",stdin);
	#endif
	scanf("%d",&n);
	for (int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);	}
	dfs(1);
	M=1;while (M-2<r[1]+1) M<<=1;
	scanf("%d",&m);
	#ifndef NDEBUG
//	cout<<M;
	for (int i=1;i<=n;i++) cout<<i<<':'<<l[i]<<'-'<<r[i]<<' ';
	cout<<'n';
	#endif
	for (int k=1;k<=m;k++)
	{
		int x;
		scanf("%s%d",s,&x);
		if (s[0]=='Q')
		{
			int ans=0,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
//			cout<<"add: ";
			#ifndef NDEBUG
			cout<<p1<<' '<<p2<<endl;
			#endif
//			cout<<mark[10];
			while (p1^p2^1)
			{
				if (~p1&1) {/*pushdown(p1+1,1);*/ans+=t[p1+1];/*cout<<p1+1<<':'<<t[p1+1]<<' ';*/}
				if (p2&1)  {/*pushdown(p2-1,1);*/ans+=t[p2-1];/*cout<<p2-1<<':'<<t[p2-1]<<' ';*/}
				p1>>=1;p2>>=1;
			}
			cout<<ans/2<<endl;
		}
		else
		{
			int p=1,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
			while (p1^p2^1)
			{
				if (~p1&1) {mark[p1+1]^=1;t[p1+1]=p-t[p1+1];/*cout<<p1+1<<' ';*/}
				if (p2&1) {mark[p2-1]^=1;t[p2-1]=p-t[p2-1];/*cout<<p2-1<<' ';*/}
				p1>>=1;p2>>=1;p<<=1;
			}
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;*/
//			update(p1);

			update(l[x]-1+M);
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;
*/			update(r[x]+1+M);

		}
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) if (mark[i]) cout<<i<<' ';
		cout<<endl;
		#endif

	}



	return 0;
}