跳跃(处理操作迭代-等价替代)

Problem2 跳跃(jump.cpp/c/pas)

【题目描述】

一开始你位于数轴上的x位置,一次跳跃你可以从x跳跃到(9x+4)mod 1000000007或者(27x+13)mod 1000000007。求跳回到原点的最少跳跃次数。

保证通过有限次跳跃可以回到原点。

【输入格式】

一行一个整数x表示你的初始坐标,保证0<=x<1000000007

【输出格式】

一行一个整数表示最小的跳跃次数。

【样例输入】

555555559

【样例输出】

1

【数据范围】

对于40%的数据,答案<=20

对于100%的数据,答案<=100000

神结论:

9x+4等价于2次3x+1

27x+13等价于3次3x+1

#include<cstdio>
#include<iostream>
#include<functional>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
#define MAXN (100000)
#define F (1000000007)
long long x;
int main()
{
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	while (scanf("%d",&x)==1)
	{
		int ans=0;
		for (;!((!x)&&ans!=1);x=(3*x+1)%F) ans++;
		printf("%dn",ans/3+bool(ans%3));
	}
	return 0;
}

BZOJ 1076([SCOI2008]奖励关-期望dp-从后向前)

1076: [SCOI2008]奖励关

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 328  Solved: 199
[Submit][Status][Discuss]

Description

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。 宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。也就是说,即使前k-1次系统都抛出宝物1(这种情况是有可能出现的,尽管概率非常小),第k次抛出各个宝物的概率依然均为1/n。 获取第i种宝物将得到Pi分,但并不是每种宝物都是可以随意获取的。第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。注意,Pi可以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。
假设你采取最优策略,平均情况你一共能在奖励关得到多少分值?

Input

第一行为两个正整数k和n,即宝物的数量和种类。以下n行分别描述一种宝物,其中第一个整数代表分值,随后的整数依次代表该宝物的各个前提宝物(各宝物编号为1到n),以0结尾。

Output

输出一个实数,保留六位小数,即在最优策略下平均情况的得分。

Sample Input

1 2

1 0

2 0

Sample Output



1.500000

HINT

【样例2】 
Input 
6 6 
12 2 3 4 5 0 
15 5 0 
-2 2 4 5 0 
-11 2 5 0 
5 0 
1 2 4 5 0 
Output 
10.023470 
【数据规模】 
1<=k<=100,1<=n<=15,分值为[-10^6,10^6]内的整数。 

Source

期望DP.

根据期望DP 这一步的期望=(上一步的期望+上一步de得分)/k
(
k为种类数)

从后往前算是规避不可能状态的常用手段


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<functional>
#include<iostream>
using namespace std;
#define MAXN (100+10)
#define MAXK (16)
double f[MAXN][(1<<MAXK)-1];
int n,k,p[MAXK+1],d[MAXK+1]={0};
int main()
{
	scanf("%d%d",&n,&k);
	int m=(1<<k)-1;
	for (int i=0;i<MAXN;i++)
		for (int j=0;j<=m;j++) f[i][j]=0.0;
	for (int i=1;i<=k;i++)
	{
		scanf("%d",&p[i]);
		int t;
		while (scanf("%d",&t)!=EOF&&t)
		{
			d[i]|=(1<<(t-1));
		}
	}
	for (int i=n;i;i--)
		for (int j=0;j<=m;j++)
		{
			for (int l=1;l<=k;l++)
				if ((d[l]&j)==d[l])
					f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(l-1))]+p[l]);//eat or not
					else f[i][j]+=f[i+1][j]; //can't eat
			f[i][j]/=(double)k;
		}
	printf("%.6lfn",f[1][0]);
	return 0;
}


UVA 10905(Children's Game-C的qsort函数和sprintf)

4th IIUC Inter-University Programming
Contest, 2005

A

Children’s Game

Input: standard input
Output: standard output

Problemsetter: Md. Kamruzzaman

给你 N 个正数. 如 123, 124, 56, 90 ,它们可以拼成 1231245690, 1241235690, 5612312490, 9012312456, 9056124123 etc. 有 24 种拼法, 9056124123 最大.

请你找出这个最大的数。

Input

每组数据第一行为 N (≤ 50). 接下来1行有 N 个正数,数据以  0 结尾.

Output

对每组数据,输出最大的拼法.

Sample Input

Output for Sample Input

4
123 124 56 90
5
123 124 56 90 9
5
9 9 9 9 9
0

9056124123
99056124123
99999


按照字符串连起来后(len相等)的字符串比较

值得一提的是qsort中cmp的写法(char[]无法用sort排序)

以及sprintf(占位符随便改,可以将任意类型转换成字符串)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXN (100+10)
int cmp(const void *a1,const void *b1)
{
	char *a=(char*)a1,*b=(char*)b1;
	char _a[MAXN*2]={0},_b[MAXN*2]={0};
	sprintf(_a,"%s%s",a,b);
	sprintf(_b,"%s%s",b,a);
	return strcmp(_b,_a);
}
int n;
char a[MAXN][MAXN];
int main()
{
	while(scanf("%d",&n)&&n)
	{
		for (int i=1;i<=n;i++) scanf("%s",a[i]);
		qsort(a+1,n,sizeof(a[1]),cmp);
		for (int i=1;i<=n;i++) printf("%s",a[i]);
		printf("n");
	}
	return 0;
}

稳定性(单向图tarjen)

Problem2 稳定性(cp.cpp/c/pas)

【题目描述】

有2*n个装置,其中奇数编号的为供电装置,偶数编号的为用电装置。

第i*2-1个装置通过单向导线第i*2个装置输送能量(它们称为第i对装置)。除此之外还有m条单向导线。

第i对装置是稳定的,当且仅当:直接连接2者的单向导线损坏时,仍然有一个供电方案使得每个供电装置给一个用电装置供电,且每个用电装置只由一个供电装置供电。

求每对装置稳定与否。

【输入格式】

第一行2个整数n,m。

接下来m行,每行2个整数a、b,表示a往b有一条单向导线。保证a为奇数,b为偶数。

【输出格式】

输出n行,若第i对装置是稳定的,输出“~”,否则输出“@”

 

【样例输入】

2 1

3 2

【样例输出】

@

@

【样例输入2】

2 2

3 2

1 4

【样例输出2】

~

~

【数据范围】

对于20%:N<=10

对于40%:N<=100

对于60%:N<=1000

对于100%:N<=100000,M<=2*N

注意Tarjen模板。

Ps:边最多有300000(原来的n条边+另外m条单向边)

#include<cstdio>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN (200000+10)
#define MAXM (300000+10)
int edge[MAXM],pre[MAXM],next[MAXM],size=0;
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=pre[u];
	pre[u]=size;
}
int t[MAXN],tim=0,dfs[MAXN];
int s[MAXN],tot=0,n,m;
int kind=0,h[MAXN];
bool b[MAXN],numk[MAXN];
void tar(int u)
{
	t[u]=dfs[u]=++tim;b[u]=1;
	s[++tot]=u;
	for (int p=pre[u];p;p=next[p])
	{
		int &v=edge[p];
		if (!b[v]) tar(v),dfs[u]=min(dfs[u],dfs[v]);
		else if (!h[v]) dfs[u]=min(dfs[u],t[v]);
	}
	if (dfs[u]==t[u])
	{
		kind++;
		bool flag=0;
		while (s[tot]!=u) h[s[tot--]]=kind,flag=1;
		h[s[tot--]]=kind;
		numk[kind]=flag;
	}
}
int main()
{
	freopen("cp.in","r",stdin);
	freopen("cp.out","w",stdout);
	scanf("%d%d",&n,&m);
	memset(pre,0,sizeof(pre));
	memset(b,0,sizeof(b));
	memset(h,0,sizeof(h));
	for (int i=1;i<=n;i++) addedge(2*i-1,2*i);
	for (int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(v,u);
	}
	for (int i=2;i<=2*n;i+=2)
	{
		if (!b[i]) tar(i);
		if (numk[h[i]]) printf("~n");
		else printf("@n");
	}

	return 0;
}

子序列(省略已知值法与挪位得解法)

Problem 1 子序列(subsequence.pas/c/cpp)

【题目描述】

给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列,

 

【输入格式】

若干行,每行表示一组数据。对于每组数据,首先输入一个整数N,表示序列的长度。之后N个整数表示这个序列。

 

【输出格式】

同输入行数。对于每组数据,如果存在一种划分,则输出“Yes!”,否则输出“No!“。

 

【样例输入】

6 3 1 4 5 8 7

6 3 2 1 6 5 4

【样例输出】

Yes!

No!

【数据范围】

共三组数据,每组数据行数<=50,0 <= 输入的所有数 <= 10^9

第一组(30%):N <= 20

第二组(30%):N <= 100

第三组(40%):N <= 2000

一般青年Dp方案:F[i][j][k][l] 表示前i+j位分为一个长度为i以j结尾,一个长度为k以l结尾的序列
是否可行(0,1)

省略已知值:观察发现j和l中至少有一个为a[i+j]

故可省略其中一位

n=2000必跪

文艺青年Dp方案:

挪位得解:把f[i][j][k]中的k挪出来 

原因:显然i和j不变时,我们希望k越小越好

所以记录min(k),并记录无解情况

O(n^2)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN (2000+10)
#define INF (2139062143)
int n,a[MAXN],f[MAXN][MAXN];
int main()
{
	freopen("subsequence.in","r",stdin);
	freopen("subsequence.out","w",stdout);
	while (scanf("%d",&n)!=EOF)
	{
		memset(f,127,sizeof(f));
		for (int i=1;i<=n;i++) scanf("%d",&a[i]);
		f[1][1]=-1;
		for (int i=1;i<n;i++)
		{
			for (int j=1;j<=i;j++)
			{
				if (f[i][j]!=INF)
				{
					if (a[i]<a[i+1]) f[i+1][j+1]=min(f[i+1][j+1],f[i][j]);
					if (f[i][j]<a[i+1])
						f[i+1][i-j+1]=min(f[i+1][i-j+1],a[i]);
				}
			}
		}
		/*
		for (int i=0;i<=n;i++)
		{
			for (int j=0;j<=n;j++)
				printf("%d ",f[i][j]);
			printf("n");
		}
		*/
		if (f[n][n>>1]!=INF) printf("Yes!n");
		else printf("No!n");
	}
	return 0;
}

数位和乘积(高精组合数学)

Problem 3 数位和乘积(digit.cpp/c/pas)

【题目描述】

一个数字的数位和乘积为其各位数字的乘积。求所有的N位数中有多少个数的数位和乘积恰好为K。请注意,这里的N位数是可以有前导零的。比如01,02视为二位数,但是他们的数位和乘积都是0。

 

【输入格式】

一行两个整数N,K

 

【输出格式】

一个行一个整数表示结果。

 

【样例输入】

2 3

【样例输出】

2

【样例输入2】

2 0

【样例输出2】

19

 

【数据范围】

对于20%:N <= 6。

对于50%:N<=16

存在另外30%:K=0。

对于100%:N <= 50,0 <= K <= 10^9。

法1:

k=0时,ans=10^n-9^n

否则就用记忆化搜索填1..9的个数

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cctype>
using namespace std;
#define MAXN (50+10)
#define F (10000)
int n,k;
struct highn
{
	int len,a[900];
	highn():len(1){memset(a,0,sizeof(a));}
	highn(int x)
	{
		memset(a,0,sizeof(a));
		int i=0;
		while (x)
		{
			a[++i]=x%F;
			x/=F;
		}
		len=i;
	}
	void print()
	{
		printf("%d",a[len]);
		for (int i=len-1;i;i--)
		{
	//		if (a[i]<10000) printf("0");
			if (a[i]<1000) printf("0");
			if (a[i]<100) printf("0");
			if (a[i]<10) printf("0");
			printf("%d",a[i]);
		}

	}
	friend highn operator+(highn& a,highn& b)
	{
		highn c;
		int maxlen=max(a.len,b.len);
		for (int i=1;i<=maxlen;i++)
		{
			c.a[i]=c.a[i]+a.a[i]+b.a[i];
			if (c.a[i]>F)
			{
				c.a[i+1]+=c.a[i]/F;
				c.a[i]%=F;
			}
		}
		c.len=maxlen+1;
		while (!c.a[c.len]&&c.len>1) c.len--;
		return c;

	}
	friend highn operator-(highn& a,highn& b)
	{
		highn c;
		int maxlen=a.len;
		for (int i=1;i<=maxlen;i++)
		{
			c.a[i]+=a.a[i]-b.a[i];
			if (c.a[i]<0)
			{
				c.a[i+1]--;
				c.a[i]+=F;
			}
		}
		c.len=maxlen;
		while (!c.a[c.len]&&c.len>1) c.len--;
		return c;
	}
	friend highn operator*(highn& a,highn& b)
	{
		highn c;
		for (int i=1;i<=a.len;i++)
		{
			for (int j=1;j<=b.len;j++)
			{
				c.a[i+j-1]+=a.a[i]*b.a[j];
				if (c.a[i+j-1]>F)
				{
					c.a[i+j]+=c.a[i+j-1]/F;
					c.a[i+j-1]%=F;
				}
			}
		}
		c.len=a.len+b.len+1;
		while (!c.a[c.len]&&c.len>1) c.len--;
		return c;
	}

};
highn pow2(highn a,int b)
{
	highn c;
	if (!b) return 1;
	if (b%2)
	{
		c=pow2(a,b/2);
		c=c*c;
		c=c*a;
	}
	else
	{
		c=pow2(a,b/2);
		c=c*c;
	}
	return c;
}
int a[11],tot,b[11];
highn ans,C[51][51];
void dfs(int deep,highn rel,int hasget)
{
	if (n<hasget) {return;}
	if (deep==0||n==hasget)
	{
		for (int i=1;i<=9;i++) if (b[i]) return;
		ans=ans+rel;
		return;
	}
	else if (deep==9)
	{
		int m=min(n-hasget,b[3]/2);
		for (int i=0;i<=m;i++)
		{
			b[3]-=i*2;
			dfs(8,rel*C[n-hasget][i],hasget+i);
			b[3]+=i*2;
		}
	}
	else if (deep==8)
	{
		int m=min(n-hasget,b[2]/3);
		for (int i=0;i<=m;i++)
		{
			b[2]-=i*3;
			dfs(6,rel*C[n-hasget][i],hasget+i);
			b[2]+=i*3;
		}
	}
	else if (deep==6)
	{
		int m=min(n-hasget,min(b[2],b[3]));
		for (int i=0;i<=m;i++)
		{
			b[2]-=i;b[3]-=i;
			dfs(4,rel*C[n-hasget][i],hasget+i);
			b[2]+=i,b[3]+=i;
		}
	}
	else if (deep==4)
	{
		int m=min(n-hasget,b[2]/2);
		for (int i=0;i<=m;i++)
		{
			b[2]-=i*2;
			dfs(2,rel*C[n-hasget][i],hasget+i);
			b[2]+=i*2;
		}
	}
	else
	{
		if (b[2]+b[3]+b[5]+b[7]+hasget<=n)
		{
			int f[4]={2,3,5,7};
			for (int i=0;i<4;i++)
			{
				rel=rel*C[n-hasget][b[f[i]]];
				hasget+=b[f[i]];
			}
			ans=ans+rel;
			return ;
		}
	}
}
int main()
{
//	highn a=1254321,b=7612345;
//	highn c=pow(a,3);
//	c.print();
	freopen("digit.in","r",stdin);
	freopen("digit.out","w",stdout);
	scanf("%d%d",&n,&k);
	if (!k)
	{
		highn c=pow2(10,n),d=pow2(9,n);
		highn e=c-d;
		e.print();
	}
	else
	{

		memset(a,0,sizeof(a));
		tot=0;
		C[0][0]=1;
		for (int i=1;i<=n;i++)
		{
			C[i][0]=C[i][1]=1;
			for (int j=1;j<=i;j++)
			{
				C[i][j]=C[i-1][j]+C[i-1][j-1];
			}
		}
//		C[16][10].print();

		for(int i=2;i<=9;i++)
		{
			while (k%i==0)
			{
				a[i]++;
				k/=i;
				tot++;
			}
		}
		memcpy(b,a,sizeof(a));
		if (k==1) dfs(9,1,0);
		ans.print();
	}
	cout<<endl;
	return 0;
}

法2:

f[i][j][k][l][m]表示填到第i个数,2,3,5,7分别用j,k,l,m个的方案数

考虑后面填1..9的情况(显然不可能填0)

Bug:n=50,C(n,m)最大为C(50,25),可用long long.

----

----

法3:

容易发现

k=2^a*3^b*5^c*7^d时才有解

而且5,7取了当且只当数位为5,7的情况.

2-2,4,6,8

3-3,6,9

5-5

7-7

故可只考虑2,3,不考虑5,7

f[i][j]k]表示i位,取了j个2和k个3后的方案数,

因为可以用1填充,

所以答案=∑f[p][j][k]*C(p,j+k)*C(n,a[5])*C(n-a[5],a[7]) (p<=n-a[5]-a[7])



BZOJ 1507([NOI2003]Editor-块状链表)

1507: [NOI2003]Editor

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 922  Solved: 323
[Submit][Status][Discuss]

Description

很久很久以前,DOS3.x 的程序员们开始对 EDLIN 感到厌倦。于是,人们开始纷纷改用自己写的文本编辑器……
多年之后,出于偶然的机会,小明找到了当时的一个编辑软件。进行了一些简单的测试后,小明惊奇地发现:那个软件每秒能够进行上万次编辑操作(当然,你不能手工进行这样的测试)!于是,小明废寝忘食地想做一个同样的东西出来。你能帮助他吗?
为了明确任务目标,小明对“文本编辑器”做了一个抽象的定义:
文本:由 0 个或多个字符构成的序列。这些字符的 ASCII 码在闭区间[32, 126]内,也就是说,这些字符均为可见字符或空格。
光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。
文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下六条操作的程序。
如果这段文本为空,我们就说这个文本编辑器是空的。

你的任务是:
 建立一个空的文本编辑器。
 从输入文件中读入一些操作指令并执行。
 对所有执行过的 GET 操作,将指定的内容写入输出文件。

Input

第一行是指令条数 t,以下是需要执行的 t 个操作。其中:
为了使输入文件便于阅读,Insert 操作的字符串中可能会插入一些回车符,请忽略掉它们(如果难以理解这句话,可以参考样例)。
除了回车符之外,输入文件的所有字符的 ASCII 码都在闭区间[32, 126]内。且行尾没
有空格。
这里我们有如下假定:
1. MOVE 操作不超过 50000 个,INSERT 和 DELETE 操作的总个数不超过 4000,PREV 和 NEXT 操作的总个数不超过 200000。
2. 所有 INSERT 插入的字符数之和不超过 2M(1M=1024*1024),正确的输出文件长度不超过 3M 字节。
3. DELETE 操作和 GET 操作执行时光标后必然有足够的字符。MOVE、PREV、NEXT操作不会把光标移动到非法位置。
4. 输入文件没有错误。
对 C++选手的提示:经测试,对最大的测试数据使用 fstream 进行输入有可能会比使用 stdio 慢约 1 秒,因此建议在可以的情况下使用后者。

Output

输出每行依次对应输入文件中每条GET指令的输出。

Sample Input

15

Insert 26

abcdefghijklmnop

qrstuv wxy

Move 16

Delete 11

Move 5

Insert 1

^

Next

Insert 1

_

Next

Next

Insert 4

./.

Get 4

Prev

Insert 1

^

Move 0

Get 22

Sample Output

./.

abcde^_^f./.ghijklmno

这题我是用块状链表做的

块状链表的复杂度大约是O(√n)

一定要尽可能将块分成长度为√tot的,balance()时要把所有的长度分为介于1/2√tot-2√tot之间

另外merge时要注意先把cur移动再重载合并格的长度(不容易查出错,不知为何,加错也能过8个点)

要参考老董的讲解与nocow的模板

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<cmath>
using namespace std;
#define MAXP (1024*1024)
#define MAXN (2900)
struct block
{
    int n;
    char a[MAXN];
    block *next,*pre;
    block():n(0){next=pre=NULL;}
}*first;
block* q[MAXN];
int size=0,tot=0;
void del(block *x)
{
    q[++size]=x;
    if (x->pre) x->pre->next=x->next;
    if (x->next) x->next->pre=x->pre;
    if (x==first) first=x->next;
}
block *newblock()
{
    if (size) return q[size--];
    block *x=new block();
    return x;
}
void link(block *a,block *b)
{
    if (a) a->next=b;
    if (b) b->pre=a;
}
void spilt(block *x,int nowsize)
{
    if (!nowsize||nowsize==x->n) return;
    block *tmp=x->next;
    x->next=newblock();
    block *x2=x->next;
    x2->n=x->n-nowsize;
    for (int i=1;i<=x2->n;i++) x2->a[i]=x->a[nowsize+i];
    x->n=nowsize;
    link(x2,tmp);
    link(x,x2);
}
struct cursor
{
    int n;
    block *ll;
    cursor():n(0){ll=first; }
    void pre()
    {
        n--;
        if (n<0)
        {
            ll=ll->pre;
            n=ll->n-1;
        }
    }
    void next()
    {
        n++;
        if (n>ll->n)
        {
            ll=ll->next;
            n=1;
        }
    }
    void move(int k)
    {
        n=k,ll=first;
        while (n>ll->n)
        {
            n-=ll->n;
            ll=ll->next;
        }
    }
}cur;
void get(cursor cur,int k)
{
    int p=cur.ll->n-cur.n;
    if (p>=k)
    {
        for (int i=cur.n+1;i<=cur.n+k;i++)
            printf("%c",cur.ll->a[i]);
    }
    else
    {
        for (int i=cur.n+1;i<=cur.ll->n;i++) printf("%c",cur.ll->a[i]);
        cur.ll=cur.ll->next;cur.n=0;
        get(cur,k-p);
    }
}
void merge(block *x)
{
    if (!x->next) return;
    for (int i=1;i<=x->next->n;i++) x->a[x->n+i]=x->next->a[i];
    if (cur.ll==x->next)
    {
        cur.ll=x;
        cur.n+=x->n;
    }
    x->n+=x->next->n;
    del(x->next);
}
void balance()
{
    int maxsize=min(MAXN,(int)sqrt(tot)*2);
    int minsize=(int)sqrt(tot)/2;
    block *x=first;
    while (x)
    {
        while (x->n<minsize||x->n>maxsize)
        {
            if (x->n<minsize)
            {
                merge(x);
                if (!x->next) break;
            //  get(cur,1);
            }
            else
            {
                spilt(x,(x->n)>>1);
                if (cur.ll==x&&cur.n>cur.ll->n)
                {
                    cur.n-=cur.ll->n;
                    cur.ll=cur.ll->next;
                }
            //  get(cur,1);
            }
        }
        x=x->next;
    }
}
void delet(int k)
{
    if (!k) return;
    if (cur.n)
    {
        spilt(cur.ll,cur.n);
        cur.ll=cur.ll->next; cur.n=0;
    }
    while (k>cur.ll->n)
    {
        del(cur.ll);
        k-=cur.ll->n;
        cur.ll=cur.ll->next;
    //  cur.n=0;
    }
    if (k==cur.ll->n)
    {
        cur.ll->n=0;
    }
    else
    {
        spilt (cur.ll,k);
        delet(k);
    }
    balance();
}
void insert(int k)
{
    block *l=newblock();*l=block();
    block *r=l;
    int maxsize=min(MAXN,(int)sqrt(tot));
    for (int i=1;i<=k;i++)
    {
        char c;
        for (scanf("%c",&c);(c<32)||(c>126)||(c=='n');scanf("%c",&c));
        if (r->n>maxsize)
        {
            block *tmp=r;
            r=newblock();*r=block();link(tmp,r);
        }
        r->a[++r->n]=c;
    }
    if (cur.n)
    {
        spilt(cur.ll,cur.n);
        block *tmp=cur.ll->next;
        link(cur.ll,l),link(r,tmp);
    }
    else
    {
        link(cur.ll->pre,l),link(r,cur.ll),cur.ll=l;
        if (first->pre) first=l;
    }
    balance();
}
int main()
{
    first=newblock();cur.ll=first;
    int t;scanf("%d",&t);
    while (t--)
    {
        char s[10];
        scanf("%s",s);
        int k;
        switch(s[0])
        {
            case'M':scanf("%d",&k);cur.move(k);break;
            case'I':scanf("%d",&k);tot+=k;insert(k);break;
            case'D':scanf("%d",&k);tot-=k;delet(k);break;
            case'G':scanf("%d",&k);get(cur,k);printf("n");break;
            case'P':cur.pre();break;
            case'N':cur.next();break;
        }
//      get(cursor(),tot);cout<<endl;
    }
    return 0;
}

UVA 10209(Is This Integration ?-容斥原理)

Problem C
Is This Integration ?
Input: Standard Input
Output: Standard Output
Time Limit: 3 seconds

 

In the image below you can see a square ABCD, where AB = BC = CD = DA = a. Four arcs are drawn taking the four vertexes A, B, C, Das centers and a as the radius. The arc that is drawn taking A as
center, starts at neighboring vertex B and ends at neighboring vertex D. All other arcs are drawn in a similar fashion. Regions of three different shapes are created in this fashion. You will have to determine the total area
if these different shaped regions.  

 

Input

The input file contains a floating-point number a (a>=0 a<=10000) in each line which indicates the length of one side of the square. Input is terminated by end of file.  

 

Output

For each line of input, output in a single line the total area of the three types of region (filled with different patterns in the image above). These three numbers will of course be floating point numbers with three digits after the decimal point. First
number will denote the area of the striped region, the second number will denote the total area of the dotted regions and the third number will denote the area of the rest of the regions.

 

Sample Input:

0.1
0.2
0.3

Sample Output:

0.003 0.005 0.002
0.013 0.020 0.007
0.028 0.046 0.016


Shahriar Manzoor

设中间那块面积为x,四个角的面积为y,四个凹形的面积为z


则有

x+4y+4z=a^2

x+3y+2z=pi/4*a^2

x+2y+z=(pi/3-√3/4)a^2 (2个扇形-中间重叠的三角形)

解出:

x=(1+pi/3-√3)*a^2

y=(pi/12+√3/2-1)*a^2

z=(-pi/6+1-√3/4)*a^2

另:注意pi的精度

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
using namespace std;
#define MAXa (10000)
double a;
const double pi=3.141592653589793;
int main()
{
	while (scanf("%lf",&a)!=EOF)
	{
		printf("%.3lf %.3lf %.3lfn",a*a*(1+pi/3-sqrt(3.0)),a*a*(pi/3+2*sqrt(3.0)-4),a*a*(-2*pi/3+4-sqrt(3.0)));
	}
	return 0;
}


BZOJ 1500([NOI2005]维修数列-Splay的数列维护)

1500: [NOI2005]维修数列

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 3087  Solved: 920
[Submit][Status][Discuss]

Description

Input

输入文件的第1行包含两个数N和M,N表示初始时数列中数的个数,M表示要进行的操作数目。 第2行包含N个数字,描述初始时的数列。 以下M行,每行一条命令,格式参见问题描述中的表格。

Output

对于输入数据中的GET-SUM和MAX-SUM操作,向输出文件依次打印结果,每个答案(数字)占一行。

Sample Input

9 8

2 -6 3 5 1 -5 -3 6 3

GET-SUM 5 4

MAX-SUM

INSERT 8 3 -5 7 2

DELETE 12 1

MAKE-SAME 3 3 2

REVERSE 3 6

GET-SUM 5 4

MAX-SUM

Sample Output

-1

10

1

10

HINT

Splay解决数列维护。

推荐:用伸展树解决数列维护问题

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cstdlib>
#include<cmath>
using namespace std;
#define MAXN (500000+10)
#define MAXM (20000+10)
#define INF (0xfffffff)
struct node
{
	int value,MaxL,MaxR,MaxM,sum,size;
	bool rev,same;
	node *ch[2],*pre;
	node(node *f,int _value,int _MaxL,int _MaxR,int _MaxM,int _sum,int _size):value(_value),MaxL(_MaxL),MaxR(_MaxR),MaxM(_MaxM),sum(_sum),size(_size){rev=same=0,pre=f,ch[0]=ch[1]=NULL;}
	node(){rev=same=0;}
	int l_siz(){if (ch[0]) return ch[0]->size;else return 0;}
//	int r_siz(){return (ch[1])?ch[1]->size:0;}

	friend void pushdown(node *x)
	{
		if (x)
		{
			if (x->rev)
			{
				x->rev=0;
				if (x->ch[0]) x->ch[0]->rev^=1;
				if (x->ch[1]) x->ch[1]->rev^=1;
				swap(x->ch[0],x->ch[1]);
				swap(x->MaxL,x->MaxR);
			}
			if (x->same)
			{
				x->same=0;
				if (x->ch[0]) {x->ch[0]->same=1;x->ch[0]->value=x->value;x->ch[0]->sum=x->value*x->ch[0]->size;x->ch[0]->MaxL=x->ch[0]->MaxR=x->ch[0]->MaxM=max(x->value,x->ch[0]->sum);}
				if (x->ch[1]) {x->ch[1]->same=1;x->ch[1]->value=x->value;x->ch[1]->sum=x->value*x->ch[1]->size;x->ch[1]->MaxL=x->ch[1]->MaxR=x->ch[1]->MaxM=max(x->value,x->ch[1]->sum);}
			}
		}
	}
	friend void update(node *x)
	{
		if (x)
		{
			pushdown(x->ch[0]);pushdown(x->ch[1]); //由于有坑爹的rev操作,必须先down.
			x->size=((x->ch[0])?x->ch[0]->size:0)+((x->ch[1])?x->ch[1]->size:0)+1;
			x->sum=((x->ch[0])?x->ch[0]->sum:0)+((x->ch[1])?x->ch[1]->sum:0)+x->value;
			x->MaxL=max(((x->ch[0])?x->ch[0]->MaxL:x->value),((x->ch[0])?x->ch[0]->sum:0)+x->value+max(0,((x->ch[1])?x->ch[1]->MaxL:0)));
			x->MaxR=max(((x->ch[1])?x->ch[1]->MaxR:x->value),((x->ch[1])?x->ch[1]->sum:0)+x->value+max(0,((x->ch[0])?x->ch[0]->MaxR:0)));
		//	cout<<((x->ch[1])?x->ch[1]->MaxR:x->value)<<' '<<+max(0,(((x->ch[0])?x->ch[0]->MaxR:0)))<<endl;
			x->MaxM=max(max(((x->ch[0])?x->ch[0]->MaxM:x->value),((x->ch[1])?x->ch[1]->MaxM:x->value)),((x->ch[0])?max(x->ch[0]->MaxR,0):0)+((x->ch[1])?max(x->ch[1]->MaxL,0):0)+x->value);
		}
	}
};
node *q[MAXN];
int q_siz=0;
int n,m,a[MAXN];
node* newnode(node *f,int value,int MaxL,int MaxR,int MaxM,int sum,int size)
{
	if (q_siz)
	{
		q[q_siz]->value=value;
		q[q_siz]->MaxL=MaxL;
		q[q_siz]->MaxR=MaxR;
		q[q_siz]->MaxM=MaxM;
		q[q_siz]->sum=sum;
		q[q_siz]->size=size;
		q[q_siz]->rev=q[q_siz]->same=0;q[q_siz]->pre=f;q[q_siz]->ch[0]=q[q_siz]->ch[1]=NULL;
		return q[q_siz--];
	}
	node *x=new node(f,value,MaxL,MaxR,MaxM,sum,size);
	return x;
}
node* newnode()
{
	if (q_siz) return q[q_siz--];
	node *x=new node();
	return x;
}
void addnode(node *x)
{
	q[++q_siz]=x;
	*x=node();
}
struct Splay
{
	node *root;
	Splay()
	{
		root=newnode(NULL,-INF,-INF,-INF,-INF,0,2);
		root->ch[0]=newnode(root,-INF,-INF,-INF,-INF,0,1);
	}
	void Print(node *x)
	{
		if (x->ch[0]) {cout<<"(",Print(x->ch[0]),cout<<")-";}
		cout<<x->same;
		if (x->ch[1]) cout<<"-(",Print(x->ch[1]),cout<<")";
	}
	void rotate(node *x,int c) //0左旋 1右旋
	{
		node *y=x->pre;
		pushdown(y),pushdown(x);
		y->ch[!c]=x->ch[c];
		if (x->ch[c]!=NULL) x->ch[c]->pre=y;
		x->pre=y->pre;
		if (y->pre!=NULL)
		{
			if (y->pre->ch[0]==y) y->pre->ch[0]=x;
			else y->pre->ch[1]=x;
		}
		x->ch[c]=y;y->pre=x;
		if (y==root) root=x;
		update(y);
	}
	void splay(node *x,node *f)
	{
		for(pushdown(x);x->pre!=f;)
		{
			if (x->pre->pre==f)
			{
				if (x->pre->ch[0]==x) rotate(x,1);
				else rotate(x,0);
			}
			else
			{
				node *y=x->pre,*z=y->pre;
				pushdown(z);pushdown(y);pushdown(x); //rev改变树结构
				if (y->ch[0]==x&&z->ch[0]==y) rotate(y,1),rotate(x,1);
				else if (y->ch[1]==x&&z->ch[1]==y) rotate(y,0),rotate(x,0);
				else if (y->ch[0]==x&&z->ch[1]==y) rotate(x,1),rotate(x,0);
				else if (y->ch[1]==x&&z->ch[0]==y) rotate(x,0),rotate(x,1);
			}
			update(x);
			//Print(root);cout<<endl;
		}
	}
	node* find_kth(node *x,int k)
	{
		pushdown(x); //确保x正确
		if (x->l_siz()>=k) return find_kth(x->ch[0],k);
		if (x->l_siz()+1==k) return x;
		else return find_kth(x->ch[1],k-x->l_siz()-1);
	}
	void build(node *f,node *x,int l,int r)
	{
		if (l>r) return;
		int m=(l+r)>>1;
		*x=node(f,a[m],a[l],a[r],a[m],a[m],1);
		if (l<m) build(x,x->ch[0]=newnode(),l,m-1);
		if (m<r) build(x,x->ch[1]=newnode(),m+1,r);
		if (l<r) update(x);
	}
	void del(node *x)
	{
		if (x->ch[0]) del(x->ch[0]);
		if (x->ch[1]) del(x->ch[1]);
		addnode(x);
	}
	void insert(int pos,int tot)
	{
		splay(find_kth(root,pos+1),NULL);
		splay(find_kth(root,pos+2),root);
		root->ch[1]->ch[0]=newnode();
		build(root->ch[1],root->ch[1]->ch[0],1,tot);
		update(root->ch[1]);update(root);
	}
	void delet(int pos,int tot)
	{
		splay(find_kth(root,pos),NULL);
		splay(find_kth(root,pos+1+tot),root);
		//Print(root);
		del(root->ch[1]->ch[0]);
		root->ch[1]->ch[0]=NULL;
		update(root->ch[1]);update(root);
	}
	void reverse(int pos,int tot)
	{
		splay(find_kth(root,pos),NULL);
		splay(find_kth(root,pos+1+tot),root);
		root->ch[1]->ch[0]->rev^=1;
		update(root->ch[1]);update(root);
	}
	void make_same(int pos,int tot,int c)
	{
		splay(find_kth(root,pos),NULL);
		splay(find_kth(root,pos+1+tot),root);
		node *x=root->ch[1]->ch[0];
		x->same=1;
		x->value=c;
		x->sum=c*x->size;
		x->MaxL=x->MaxR=x->MaxM=max(x->value,x->sum);
		update(root->ch[1]);update(root);
	}
	void get_sum(int pos,int tot)
	{
		if (tot==0)
		{
			printf("0n");
			return;
		}
		splay(find_kth(root,pos),NULL);
		//Print(root);cout<<endl;
		splay(find_kth(root,pos+1+tot),root);
		//Print(root);cout<<endl;
		node *x=root->ch[1]->ch[0];
		printf("%dn",x->sum);
	}
	void max_sum()
	{
		splay(find_kth(root,1),NULL);
		splay(find_kth(root,root->size),root);
		printf("%dn",root->ch[1]->ch[0]->MaxM);
	}
}S;
int main()
{
//	freopen("bzoj1500.in","r",stdin);
	cin>>n>>m;
	for (int i=1;i<=n;i++) cin>>a[i];
	S.insert(0,n);
	//S.Print(S.root);cout<<endl;
	for (int i=1;i<=m;i++)
	{
	//	cout<<i<<':';
		char s[10];
		scanf("%s",s);
		switch (s[2])
		{
			case'S':
				{
					int pos,tot;
					scanf("%d%d",&pos,&tot);
					for (int i=1;i<=tot;i++) scanf("%d",&a[i]);
					S.insert(pos,tot);
					break;
				}
			case'L':
				{
					int pos,tot;
					scanf("%d%d",&pos,&tot);
					S.delet(pos,tot);
					break;
				}
			case'K':
				{
					int pos,tot,c;
					scanf("%d%d%d",&pos,&tot,&c);
					S.make_same(pos,tot,c);
					break;
				}
			case'V':
				{
					int pos,tot;
					scanf("%d%d",&pos,&tot);
					S.reverse(pos,tot);
					break;
				}
			case'T':
				{
					int pos,tot;
					scanf("%d%d",&pos,&tot);
					S.get_sum(pos,tot);
					break;
				}
			case'X':
				{
					S.max_sum();
					break;
				}
		}
		//S.Print(S.root);cout<<endl;
	}
	return 0;
}

BZOJ 1034([ZJOI2008]泡泡堂BNB-田忌赛马)

1034: [ZJOI2008]泡泡堂BNB

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 682  Solved: 321
[Submit][Status][Discuss]

Description

 第XXXX届NOI期间,为了加强各省选手之间的交流,组委会决定组织一场省际电子竞技大赛,每一个省的代表队由n名选手组成,比赛的项目是老少咸宜的网络游戏泡泡堂。每一场比赛前,对阵双方的教练向组委会提交一份参赛选手的名单,决定了选手上场的顺序,一经确定,不得修改。比赛中,双方的一号选手,二号选手……,n号选手捉对厮杀,共进行n场比赛。每胜一场比赛得2分,平一场得1分,输一场不得分。最终将双方的单场得分相加得出总分,总分高的队伍晋级(总分相同抽签决定)。
作为浙江队的领队,你已经在事先将各省所有选手的泡泡堂水平了解的一清二楚,并将其用一个实力值来衡量。为简化问题,我们假定选手在游戏中完全不受任何外界因素干扰,即实力强的选手一定可以战胜实力弱的选手,而两个实力相同的选手一定会战平。由于完全不知道对手会使用何种策略来确定出场顺序,所以所有的队伍都采取了这样一种策略,就是完全随机决定出场顺序。 当然你不想这样不明不白的进行比赛。你想事先了解一下在最好与最坏的情况下,浙江队最终分别能得到多少分。

Input

输入的第一行为一个整数n,表示每支代表队的人数。 接下来n行,每行一个整数,描述了n位浙江队的选手的实力值。 接下来n行,每行一个整数,描述了你的对手的n位选手的实力值。 20%的数据中,1<=n<=10; 40%的数据中,1<=n<=100; 60%的数据中,1<=n<=1000; 100%的数据中,1<=n<=100000,且所有选手的实力值在0到10000000之间。

Output

包括两个用空格隔开的整数,分别表示浙江队在最好与最坏的情况下分别能得多少分。不要在行末输出多余的空白字符。

Sample Input

2

1

3

2

4

Sample Output

2 0

样例说明

我们分别称4位选手为A,B,C,D。则可能出现以下4种对战方式,最好情况下可得2分,最坏情况下得0分。

一 二 三 四

浙江 ??? 结果 浙江 ??? 结果 浙江 ??? 结果 浙江 ??? 结果

一号选手 A C 负 A D 负 B C 胜 B D 负

二号选手 B D 负 B C 胜 A D 负 A C 负

总得分 0 2 2 0



田忌赛马加强版。

尽可能让最弱的赢,最强的赢,都不行则最弱打最强

错误的贪心:

让最弱的能赢/平则赢/平,否则让其与最强的打

反例:

2

1 2 6 7

1 2 4 7

 ans1=6

反例ans1=5


最差情况:

容易发现2个队伍的总分一定(2-0,1-1,0-2)

 所以ans2=2n-B队最高分

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cstdlib>
#include<cmath>
using namespace std;
#define MAXN (100000+10)
int n,a[MAXN],b[MAXN];
int calc()
{
	int i=1,j=1,k=n,l=n,ans=0;
	while (i<=k)
	{
		if (a[i]>b[j]) ans+=2,i++,j++;
		else if(a[k]>b[l]) ans+=2,k--,l--;
		else ans+=(a[i]==b[l]),i++,l--;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=1;i<=n;i++) scanf("%d",&b[i]);
	sort(a+1,a+1+n);sort(b+1,b+1+n);
	cout<<calc()<<' ';
	swap(a,b);
	cout<<2*n-calc()<<endl;
	return 0;
}