银河之星(记忆化搜索+9点染色)

Problem 3 银河之星(galaxy.cpp/c/pas)

数据组数不超过10.

这题就是记忆化搜索

9点染色减少状态,map记忆化

b[i][j][k]表示棋子可否从k方向到(i,j)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cmath>
#include<map>
using namespace std;
#define MAXN (100+10)
#define MAXK (10+10)
#define MAXDIV (4)
int k,n,m,x2,y2,a[MAXDIV][MAXDIV],b[MAXDIV][MAXDIV][9];
int c[8][2]={{0,1},{0,2},{1,0},{2,0},{1,1},{2,2},{1,2},{2,1}};  //↑↓→←↗↙↘↖
map<long long,int> h;
bool is_ok()
{
	int ans=0;
	for (int i=0;i<3;i++)
		for (int j=0;j<3;j++)
			ans=(11*ans+a[i][j]);
	if (h.find(ans)!=h.end()) return 0;
	return h[ans]=1;
}
bool dfs(int x)
{
	/*
	for (int j=3;j;j--)
		{
			for (int i=1;i<=3;i++)
				cout<<a[i%3][j%3];
			cout<<endl;
		}
		cout<<endl;
	*/
	if (!is_ok()) return 0;
	if (x==1)
	{
		if (a[x2][y2]) return 1;
		else return 0;
	}
	for (int i=0;i<3;i++)
		for (int j=0;j<3;j++)
			if (a[i][j])
			{
				for (int l=0;l<8;l++)
					if (a[(i+c[l][0])%3][(j+c[l][1])%3]&&a[(i+2*c[l][0])%3][(j+2*c[l][1])%3]<b[(i+2*c[l][0])%3][(j+2*c[l][1])%3][l])
					{
						a[i][j]--;a[(i+c[l][0])%3][(j+c[l][1])%3]--;a[(i+2*c[l][0])%3][(j+2*c[l][1])%3]++;
						if (dfs(x-1)) return 1;
						a[i][j]++;a[(i+c[l][0])%3][(j+c[l][1])%3]++;a[(i+2*c[l][0])%3][(j+2*c[l][1])%3]--;

					}
			}
	return 0;
}
int main()
{
	freopen("galaxy.in","r",stdin);
	freopen("galaxy.out","w",stdout);
	while (scanf("%d%d%d%d%d",&k,&n,&m,&x2,&y2)!=EOF)
	{
		x2%=3;y2%=3;
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		for (int i=0;i<3;i++)
			for (int j=0;j<3;j++)
			{
				int h=((n/3)+(i>0)*(i<=n%3)),r=((m/3)+(j>0)*(j<=m%3));
				//b[i][j][8]=((n/3)+(i>0)*(i<=n%3))*((m/3)+(j>0)*(j<=m%3));
				b[i][j][8]=h*r;
				if (j!=0) b[i][j][0]=max(r-1,0)*h;else b[i][j][0]=r*h;
				if ((m+1)%3!=j) b[i][j][1]=max(r-1,0)*h;else b[i][j][1]=r*h;
				if (i!=0) b[i][j][2]=max(h-1,0)*r;else b[i][j][2]=r*h;
				if ((n+1)%3!=i) b[i][j][3]=max(h-1,0)*r;else b[i][j][3]=r*h;
				b[i][j][4]=max(r-(j!=0),0)*max(h-(i!=0),0);
				b[i][j][5]=max(r-((m+1)%3!=j),0)*max(h-((n+1)%3!=i),0);
				b[i][j][6]=max(r-((m+1)%3!=j),0)*max(h-(i!=0),0);
				b[i][j][7]=max(r-(j!=0),0)*max(h-((n+1)%3!=i),0);
			}

		for (int i=1;i<=k;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			a[x%3][y%3]++;
		}
		/*
		for (int j=3;j;j--)
		{
			for (int i=1;i<=3;i++)
				cout<<a[i%3][j%3];
			cout<<endl;
		}
		cout<<endl;
		*/
		if (dfs(k)) cout<<"Yesn";
		else cout<<"Non";
		h.clear();
	}
	return 0;
}



BZOJ 1084([SCOI2005]最大子矩阵-长矩阵Dp)

1084: [SCOI2005]最大子矩阵

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 586  Solved: 275
[Submit][Status][Discuss]

Description

这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大。注意:选出的k个子矩阵不能相互重叠。

Input

第一行为n,m,k(1≤n≤100,1≤m≤2,1≤k≤10),接下来n行描述矩阵每行中的每个元素的分值(每个元素的分值的绝对值不超过32767)。

Output

只有一行为k个子矩阵分值之和最大为多少。

Sample Input

3 2 2

1 -3

2 3

-2 3

Sample Output

9

由于m不超过2
可以暴力Dp
分为m=1和m=2种情况
m=1就不用说了,
F[i][j][k]表示第1行到i第二行到j所能取到的最小值

#include<cstdio>
#include<cstring>
#include<iostream>
#include<functional>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define MAXN (100+10)
#define MAXM (2+1)
#define MAXK (10+1)
int f[MAXN][MAXK],F[MAXN][MAXN][MAXK],n,m,k;
int a[MAXN][MAXN],sum[MAXN][MAXN];
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	sum[0][1]=sum[0][2]=0;
	for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%d",&a[i][j]),sum[i][j]=sum[i-1][j]+a[i][j];
	if (m==1)
	{
		memset(f,0,sizeof(f));
		for (int i=1;i<=n;i++)
		{
			f[i][0]=0;
			for (int l=1;l<=k;l++)
			{
				f[i][l]=f[i-1][l];
				for (int j=0;j<i;j++)
				{
					f[i][l]=max(f[i][l],f[j][l-1]+sum[i][1]-sum[j][1]);
				}
			}
		}
		cout<<f[n][k]<<endl;
	}
	else
	{
		memset(F,0,sizeof(F));
		for (int i=1;i<=n;i++)
		{
			for (int j=1;j<=n;j++)
			{
				F[i][j][0]=0;
				for (int l=1;l<=k;l++)
				{
					F[i][j][l]=max(F[i-1][j][l],F[i][j-1][l]);
					for (int i2=0;i2<i;i2++) F[i][j][l]=max(F[i][j][l],F[i2][j][l-1]+sum[i][1]-sum[i2][1]);
					for (int j2=0;j2<j;j2++) F[i][j][l]=max(F[i][j][l],F[i][j2][l-1]+sum[j][2]-sum[j2][2]);
					if (i==j)
						for (int j2=0;j2<i;j2++)
						{
							F[i][i][l]=max(F[i][i][l],F[j2][j2][l-1]+sum[i][1]-sum[j2][1]+sum[j][2]-sum[j2][2]);
						}
				}
			}
		}

		cout<<F[n][n][k]<<endl;

	}


	return 0;
}

CF 23E(Tree-树-背包合并)

Problem 2 树(tree.cpp/c/pas)

【题目描述】

L发明了一种与树有关的游戏(友情提醒:树是一个没有环的连通图):他从树中删除任意数量(可以为0)的边,计算删除后所有连通块大小的乘积,L将得到这么多的分数。你的任务就是对于一颗给定的树,求出L能得到的最大分数。

【输入格式】

第一行一个整数n,表示树的节点个数。
  接下来n-1行,每行两个整数a[i],b[i](1<=a[i],b[i]<=n),表示a[i]与b[i]之间连边。
  保证输入的图是一棵树。

【输出格式】

输出一个整数,表示L能得到的最大分数。

 【样例输入】

样例1:
5
1 2
2 3
3 4
4 5
样例2:
8
1 2
1 3
2 4
2 5
3 6
3 7
6 8
样例3:
3
1 2
1 3

【样例输出】

样例1:
6
样例2:
18
样例3:
3

【数据范围】

    对于10%的数据,1<=n<=5;
  对于30%的数据,1<=n<=100;
  另有30%的数据,保证数据是一条链。
  对于100%的数据,1<=n<=700;

 

树上背包

f[i][j]表示i的父亲的连通块在子树i中有j个的最大的最大值。

于是这就是树形Dp+背包合并了、

背包合并2个先合并,再与第三个……

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<iostream>
#include<cstdlib>
#include<cmath>
using namespace std;
#define MAXN (700+10)
#define ll long long
#define F (100000000)
int n,edge[MAXN*2],pre[MAXN],next[MAXN*2],size=0,son[MAXN];
struct bign
{
	ll a[40];
	bign(){memset(a,0,sizeof(a));a[0]=1;}
	bign(int x){memset(a,0,sizeof(a)); a[0]=1;a[1]=x;	}
	ll& operator[](const int i){return a[i];	}
	friend bign operator*(bign a,bign b)
	{
		bign c;
		for (int i=1;i<=a[0];i++)
			for (int j=1;j<=b[0];j++)
			{
				c[i+j-1]+=a[i]*b[j];
				if (c[i+j-1]>F)
				{
					c[i+j]+=c[i+j-1]/F;
					c[i+j-1]%=F;
				}
			}
		c[0]=min(a[0]+b[0],(long long)39);while (!c[c[0]]&&c[0]>1) c[0]--;
		return c;
	}
	friend bool operator>(bign a,bign b)
	{
		if (a[0]!=b[0]) return a[0]>b[0];
		for (int i=a[0],j=b[0];i>0;i--,j--) if (a[i]!=b[j]) return a[i]>b[j];
		return false;
	}
	void print()
	{
		printf("%I64d",a[a[0]]);
		for (int i=a[0]-1;i;i--)
		{
			printf("%.8I64d",a[i]);
		}
	}
}f[MAXN][MAXN];
bign max(bign a,bign b)
{
	if (a>b) return a;
	return b;
}
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=pre[u];
	pre[u]=size;
}
void dfs(int x,int father)
{
	son[x]=1;//f[x][1]=1;
	f[x][1]=1;
	for (int p=pre[x];p;p=next[p])
	{
		int &v=edge[p];
		if (v!=father)
		{
			dfs(v,x);
			/*
			for (int i=son[x]+son[v];i>0;i--)
			{
				if (i<son[x]) f[x][i]=f[x][i]*son[v];
				for (int k=son[v];k>=0;k--)
					if (i-k-1>=0) f[x][i]=max(f[x][i],f[x][i-k-1]*f[v][k]);

			}
			son[x]+=son[v];
			bign maxv=son[v];
			for (int k=0;k<=son[v]-1;k++) maxv=max(maxv,f[v][k]*(k+1));
			f[x][0]=f[x][0]*maxv;
			*/
			for (int i=son[x];i;i--)
				for (int j=son[v];j>=0;j--)
					f[x][i+j]=max(f[x][i+j],f[x][i]*f[v][j]);
			son[x]+=son[v];
		}
	}
	f[x][0]=bign(son[x]);
	for (int i=1;i<=son[x];i++) f[x][0]=max(f[x][0],f[x][i]*bign(i));




	return;
}
int main()
{
//	freopen("tree.in","r",stdin);
//	freopen("tree.out","w",stdout);
	scanf("%d",&n);
	memset(pre,0,sizeof(pre));
	memset(next,0,sizeof(next));
	for (int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		addedge(u,v);addedge(v,u);
	}
	addedge(n+1,1);
	dfs(n+1,0);	//n+1 is ans
	/*
	for (int i=1;i<=n+1;i++)
	{
		for (int j=0;j<=son[i]-1;j++)
		{
			f[i][j].print();printf(" ");
		}
		printf("n");
	}
	*/
//	f[n+1][1]=bign(123456789)*bign(234567899);

	f[1][0].print();
	printf("n");

	return 0;
}

RQNOJ 698(矩形计数-圆内接矩形数)

查看题目 Show Problem

题目:矩形计数

问题编号:698

题目描述

给出圆周上的N个点,请你计算出以这些点中的任意四个为四个角,能构成多少个矩形。
点的坐标是这样描述的,给定一个数组v[1..N],假设圆心为(0,0),圆的周长C=∑v[1..N] ,第一个点坐标为(0,C/(2π))。从第一个点开始,顺时针沿圆周走v1个单位长度,此时坐标为第二个点的坐标,再走v2个单位长度,此时为第三个点的坐标,当走完v1,v2..vi个距离后,为第i+1个点的坐标(全过程都是沿圆周顺时针)。特别的,走完v1,v2..vn个距离后,就会回到第一个点。

对于100%的数据,有N<=20,V数组中的所有元素的值<=100。

输入格式

输入共N+1行。
第一行为正整数N。
接下来N行每行一个正整数。其中第i+1行表示的是v[i]。

输出格式

输出共1行,一个整数,表示能构成的矩形的个数。

样例输入

8
1
2
2
3
1
1
3
3

样例输出

3

//样例解释[img]http://www.rqnoj.cn/ProblemPic/P698-101656.png[/img]

观察图片,易发现圆内的内接矩形对角线交点必过圆心(初中证明……)

所以只要找到所有对角线就行了


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
#include<cstdlib>
using namespace std;
#define MAXN (100+10)
int n,c=0,m=0,a[MAXN];
int main()
{
	scanf("%d",&n);
	a[1]=0;
	for (int i=2;i<=n;i++)
	{
		scanf("%d",&a[i]),c+=a[i];
		if (!a[i]) i--,n--;
		else a[i]=c;
	}
	int p;
	scanf("%d",&p);c+=p;
	int l=1,r=1;
	while (r<=n&&l<=n)
	{
		if (a[r]-a[l]==c/2) {/*cout<<l<<' '<<r<<endl;*/m++;l++;}
		else if (a[r]-a[l]<c/2) r++;
		else l++;
	}
	cout<<m*(m-1)/2<<endl;
	return 0;
}

UVA 11729(Commando War-按执行时间贪心)




G

Commando War

Input: Standard Input

Output: Standard Output

 

 

“Waiting for orders we held in the wood, word from the front never came

By evening the sound of the gunfire was miles away

Ah softly we moved through the shadows, slip away through the trees

Crossing their lines in the mists in the fields on our hands and our knees

And all that I ever, was able to see

The fire in the air, glowing red, silhouetting the smoke on the breeze”

 

There is a war and it doesn't look very promising for your country. Now it's time to act. You have a commando squad at your disposal and planning an ambush on an important enemy camp located nearby. You have soldiers in your squad. In
your master-plan, every single soldier has a unique responsibility and you don't want any of your soldier to know the plan for other soldiers so that everyone can focus on his task only. In order to enforce this, you brief every individual soldier about his
tasks separately and just before sending him to the battlefield. You know that every single soldier needs a certain amount of time to execute his job. You also know very clearly how much time you need to brief every single soldier. Being anxious to finish
the total operation as soon as possible, you need to find an order of briefing your soldiers that will minimize the time necessary for all the soldiers to complete their tasks. You may assume that, no soldier has a plan that depends on the tasks of his fellows.
In other words, once a soldier  begins a task, he can finish it without the necessity of pausing in between.

 

Input

 

There will be multiple test cases in the input file. Every test case starts with an integer N (1<=N<=1000), denoting the number of soldiers. Each of the following N lines describe a soldier with two integers B (1<=B<=10000) J
(1<=J<=10000)
seconds are needed to brief the soldier while completing his job needs seconds. The end of input will be denoted by a case with N =0 . This case should not be processed.

 

Output

 

For each test case, print a line in the format, “Case X: Y”, where X is the case number & Y is the total number of seconds counted from the start of your first briefing till the completion of all jobs.

 

Sample Input                                               Output for Sample Input

3

2 5

3 2

2 1

3

3 3

4 4

5 5

0

Case 1: 8

Case 2: 15

 


Problem Setter: Mohammad Mahmudur Rahman, Special Thanks: Manzurur Rahman Khan


按照执行时间排序,贪心

可以画图验证。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (1000+10)
#define MAXBi (10000+10)
#define MAXJi (10000+10)
struct arr
{
	int a,b;
	friend bool operator<(arr a,arr b){return a.b>b.b;	}
}a[MAXN];
int n;
int main()
{
	int t=1;
	while (scanf("%d",&n)!=EOF&&n)
	{
		for (int i=1;i<=n;i++) scanf("%d%d",&a[i].a,&a[i].b);
		sort(a+1,a+n+1);
		int p=0,ans=0;
		for (int i=1;i<=n;i++)
		{
			p+=a[i].a;
			ans=max(ans,p+a[i].b);
		}
		printf("Case %d: %dn",t++,ans);
	}
	return 0;
}

回文子串对(扩展kmp-kmp与回文子串)

Problem 1 回文子串对(manacher.cpp/c/pas)

【题目描述】

给定一长度为n的小写字母串,求有多少对回文子串,它们的交集非空。

一对回文子串的交集非空:[a,b]、[c,d](a≠c或b≠d)为2个回文子串,且[a,b]∩[c,d]≠∅。

【输入格式】

第一行一个整数n表示串长。

第二行长度为n的小写字母串。

【输出格式】

输出一个整数表示答案,答案对1000000007取模。

【样例输入】

4

babb

【样例输出】

6

【数据范围】

对于30%的数据,n<=1000

另有10%的数据,串里仅含一种字母。

对于100%的数据,n<=2*10^6

 

找到最前面的max(r[j]+j),映射过去

设r[i]表示以i点为中心点的最长回文子串

则如图:



#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<functional>
#include<algorithm>
using namespace std;
#define F (1000000007)
#define MAXN (2000000+10)
using namespace std;
long long r[MAXN],n,L[MAXN][2],R[MAXN][3];
char s[MAXN];
int main()
{
	freopen("manacher.in","r",stdin);
	freopen("manacher.out","w",stdout);
	scanf("%d%s",&n,s+1);
	memset(r,0,sizeof(r));
	memset(L,0,sizeof(L));
	memset(R,0,sizeof(R));
	int j=0;
	for (int i=1;i<=n;i++)
	{
		if (r[j]+j>i) r[i]=min(r[j-(i-j)],j+r[j]-i);
		while (i-r[i]>1&&i+r[i]<n&&s[i-r[i]-1]==s[i+r[i]+1]) r[i]++;
		if (r[i]+i>r[j]+j) j=i;
		L[i-r[i]][0]+=1;
		L[i+1][0]-=1;
		R[i][0]++;
		R[i+r[i]+1][0]--;
	}
//	for (int i=1;i<=n;i++) cout<<r[i]<<' ';cout<<endl;
	j=0;memset(r,0,sizeof(r));
	for (int i=1;i<n;i++)
	{
		if (r[j]+j>i) r[i]=min(r[j-(i-j)],j+r[j]-i);
		while (i-r[i]>0&&i+r[i]<=n&&s[i+1-r[i]-1]==s[i+r[i]+1]) r[i]++;
		if (r[i]+i>r[j]+j) j=i;
		L[i+1-r[i]][0]+=1;
		L[i+1][0]-=1;
		R[i+1][0]++;
		R[i+r[i]+1][0]--;
	}
/*
	for (int i=1;i<=n;i++) cout<<L[i][0]<<' ';cout<<endl;
	for (int i=1;i<=n;i++) cout<<R[i][0]<<' ';cout<<endl;
*/
	for (int i=1;i<=n;i++) L[i][1]=L[i][0]+L[i-1][1];
	for (int j=1;j<=2;j++)
		for (int i=1;i<=n;i++)
			R[i][j]=R[i-1][j]+R[i][j-1];
	long long ans=0;
	for (int i=1;i<=n;i++)
		ans=(ans+L[i][1]*R[i-1][2])%F;
	long long tot=0;
	if (R[n][2]%2) tot=(((R[n][2]-1)/2)%F)*((R[n][2])%F)%F;
	else tot=(((R[n][2])/2)%F)*((R[n][2]-1)%F)%F;
//	cout<<tot<<' '<<ans<<endl;

	cout<<((tot-ans+F)%F)<<endl;
	return 0;
}




放球游戏(a^b博弈)

Problem 3 放球游戏(ball.cpp/c/pas)

【题目描述】

     Stas和Masha发明了一个游戏。游戏道具是a个两两不同的箱子和b个两两不同的皮球,Stas和Masha轮流操作,且每次操作新增一个完全不同的箱子或皮球。如果Stas或Masha操作了以后,把b个皮球放进a个箱子的方案数不小于n,那么这个人就会输掉。所有箱子和皮球都是不同的,可以有空的箱子。
  如果第一回合由Stas先操作,且Stas和Masha都按照最优策略进行游戏,那么是否会打平?如果不打平,谁会输??

【输入格式】

输入的第一行包含三个正整数a,b,n。

【输出格式】

如果Stas会输,输出'Stas'(不要输出引号);如果Masha会输,输出'Masha';   如果游戏会打平(也就是不结束),输出'Missing'。

【样例输入】

样例一:2 2 10

样例二:5 5 16808

样例三:3 1 4

样例四:1 4 10

 

【样例输出】

样例一:Masha

样例二:Masha

样例三:Stas

样例四:Missing

【数据范围】

对于10%的数据,n不超过10;
    对于30%的数据,n不超过10000。

对于100%的数据,1≤a≤10,000, 1≤b≤30, 2≤n≤1,000,000,000, a^b<n

博弈a^b=n

把a>1的所用情况记忆化(情况数少)

唯一平局的情况是

a=1 b=x 

(a+1)^b爆 

还有一种是

1^x 2^x爆 判奇偶

其实不记忆化也能过(汗-_-)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
#define MAXN (1000000000)
#define MAXA (10000+10)
#define MAXB (30+10)
long long a,b,n;
bool pow2(long long a,long long b)
{
	long long ans=1;
	for(int i=1;i<=b;i++)
	{
		ans*=a;
		if (ans>=n) return 0;
	}
	return 1;
}
int dfs(long long a,long long b) //a,b状态保证合法 0 Miss 1 Win -1 Lost
{
	if (a==1&&!pow2(a+1,b)) return 0;
	int flag=-1;
	if (a==1)
	{
		if (pow2(a+1,b)) flag=max(-dfs(a+1,b),flag);
		if (flag==1) return 1;
		if (pow2(a,b+1)) flag=max(-dfs(a,b+1),flag);
		return flag;
	}
	else
	{
		if (pow2(a,b+1)) flag=max(-dfs(a,b+1),flag);
		if (flag==1) return 1;
		if (pow2(a+1,b)) flag=max(-dfs(a+1,b),flag);
		return flag;
	}
}
int main()
{
	freopen("ball.in","r",stdin);
	freopen("ball.out","w",stdout);
	scanf("%d%d%d",&a,&b,&n);
	/*
	if (a==1&&!pow2(a+1,b))
	{

		return 0;
	}
	else if (b==1&&!dfs(a,b+1))
	{

	}
	*/
	int p=dfs(a,b);
	if (p==0) printf("Missingn");
	else if (p==-1) printf("Stasn");
	else printf("Mashan");
	return 0;
}

BZOJ 1502([NOI2005]月下柠檬树-Simpson积分)

1502: [NOI2005]月下柠檬树

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 250  Solved: 148
[Submit][Status][Discuss]

Description

李哲非常非常喜欢柠檬树,特别是在静静的夜晚,当天空中有一弯明月温柔地照亮地面上的景物时,他必会悠闲地坐在他亲手植下的那棵柠檬树旁,独自思索着人生的哲理。
李哲是一个喜爱思考的孩子,当他看到在月光的照射下柠檬树投在地面上的影子是如此的清晰,马上想到了一个问题:树影的面积是多大呢?
李哲知道,直接测量面积是很难的,他想用几何的方法算,因为他对这棵柠檬树的形状了解得非常清楚,而且想好了简化的方法。
李哲将整棵柠檬树分成了 n 层,由下向上依次将层编号为 1,2,...,n。从第 1到 n-1 层,每层都是一个圆台型,第 n 层(最上面一层)是圆锥型。对于圆台型,其上下底面都是水平的圆。对于相邻的两个圆台,上层的下底面和下层的上底面重合。第 n 层(最上面一层)圆锥的底面就是第 n-1 层圆台的上底面。所有的底面的圆心(包括树顶)处在同一条与地面垂直的直线上。李哲知道每一层的高度为h1,h2,...,hn,第 1 层圆台的下底面距地面的高度为 h0,以及每层的下底面的圆的半径 r1,r2,...,rn。李哲用熟知的方法测出了月亮的光线与地面的夹角为
alpha。

Input

文件的第1行包含一个整数n和一个实数alpha,表示柠檬树的层数和月亮的光线与地面夹角(单位为弧度)。 第2行包含n+1个实数h0,h1,h2,…,hn,表示树离地的高度和每层的高度。 第3行包含n个实数r1,r2,…,rn,表示柠檬树每层下底面的圆的半径。 上述输入文件中的数据,同一行相邻的两个数之间用一个空格分隔。 输入的所有实数的小数点后可能包含1至10位有效数字。

Output

输出1个实数,表示树影的面积。四舍五入保留两位小数。

Sample Input

2 0.7853981633

10.0 10.00 10.00

4.00 5.00

Sample Output

171.97

HINT

1≤n≤500,0.3<alpha<π 2,0<hi≤100,0<ri≤100。
 10%的数据中,n=1。
30%的数据中,n≤2。
60%的数据中,n≤20。
100%的数据中,n≤500。

这题是Simpson模版题。
有一个关键是圆的公切线的求法。
首先延长圆的切线求l,再求θ,最后用相似三角形。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define MAXN (500+10)
#define eps 1e-8
double h[MAXN],s[MAXN],r[MAXN],alpha,l1=0,r1=0;
double sqr(double x){return x*x;}
int n,size=0;
struct S
{
	double x,y,p,q;
}c[MAXN];
double f(double l)
{
	double t=0.0;
	for (int i=0;i<n;i++)
	{
		if (fabs(s[i]-l)<r[i]) t=max(t,sqrt(sqr(r[i])-sqr(s[i]-l)));
	}
	for (int i=1;i<=size;i++)
	{
		if (c[i].x<l&&l<c[i].p)	t=max(t,c[i].y+(c[i].q-c[i].y)*(l-c[i].x)/(c[i].p-c[i].x));
	}
	return t;

}
double simpson(double l,double r,double fl,double fmid,double fr)
{
	return (fl+4*fmid+fr)*(r-l)/6;
}
double rsimpson(double l,double r,double fl,double fmid,double fr)
{
	double m=(l+r)/2;
	double p=f((l+m)/2),q=f((m+r)/2);
	double x=simpson(l,r,fl,fmid,fr),y=simpson(l,m,fl,p,fmid),z=simpson(m,r,fmid,q,fr);
	if (fabs(x-y-z)<eps)
		return y+z;
	return rsimpson(l,m,fl,p,fmid)+rsimpson(m,r,fmid,q,fr);
}
int main()
{
	scanf("%d%lf",&n,&alpha);
	alpha=1/tan(alpha);
	for (int i=0;i<=n;i++)
	{
		scanf("%lf",&h[i]);
		if (i) h[i]+=h[i-1];
		s[i]=h[i]*alpha;
	}
	for (int i=0;i<n;i++)
	{
		scanf("%lf",&r[i]);
		l1=min(l1,s[i]-r[i]);
		r1=max(r1,s[i]+r[i]);
	}
	r[n]=0;
	for (int i=0;i<n;i++)
	{
		double d=s[i+1]-s[i];
		if (d>fabs(r[i]-r[i+1]))
		{
			c[++size].x=s[i]-r[i]*(r[i+1]-r[i])/d;
			c[size].y=sqrt(sqr(r[i])-sqr(c[size].x-s[i]));
			c[size].p=s[i+1]-r[i+1]*(r[i+1]-r[i])/d;
			c[size].q=sqrt(sqr(r[i+1])-sqr(c[size].p-s[i+1]));
		}
	}
	r1=max(r1,s[n]);
	printf("%.2lf",2*rsimpson(l1,r1,0,f((l1+r1)/2),0));
	return 0;
}

两个子序列(?标识符)

Problem 1 两个子序列(twosub.pas/c/cpp)

【题目描述】

将给定的n个长度为M的01串序列{a1,a2…an} 分为两个子序列{b1,b2…bk}和{c1,c2…cm},m+k=n,使|f(b1,b2…bk)|+|f(c1,c2…cm)|最小化。

f函数定义如下:

f(空序列)=空串
       f(s)=s
       f(s1,s2)=最小长度的有一个前缀等于s1并且有一个后缀等于s2的字符串。        f(a1,a2,…,an)=f(f(a1,a2,…,an-1),an)。

 

【输入格式】

第一行两个整数n。

接下来n行每行一个长度为M的01串,表示a1,a2...an。

 

【输出格式】

输出一个整数,表示答案。

 

【样例输入】

4
000
111
110
001

【样例输出】

8

【数据范围】

对于30%的数据,n<=20

对于60%的数据,n<=2000

对于100%的数据,1<=n<=200000,1<=M<=20

 

 这题要用后缀DP

f[i][j]表示非当前枚举点结尾的那个字符串结尾为i



#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cmath>
#include<iostream>
using namespace std;
#define MAXN (200000+10)
#define MAXM (20+10)
#define MAXSum (1<<20)
int n,m,f[MAXSum][MAXM],a[MAXN],bin[MAXM];
char s[MAXM];
int main()
{
	freopen("twosub.in","r",stdin);
	freopen("twosub.out","w",stdout);
	scanf("%d",&n);
	memset(f,128,sizeof(f));
	memset(a,0,sizeof(a));
	for (int i=1;i<=n;i++)
	{
		scanf("%s",s);
		m=strlen(s);
		for (int j=0;j<m;j++) a[i]=(a[i]<<1)+s[j]-48;
	}
	int tot=0;
	bin[0]=0;
	for (int i=1;i<=20;i++) bin[i]=(1<<i)-1;
	int ans=n*m;
	for (int i=2;i<=n;i++)
	{
		int k=m;
		while (k&&((bin[k]&a[i-1])!=a[i]>>(m-k)))
		{
		//	cout<<(bin[k]&a[i-1])<<' '<<a[i]<<m-k<<(a[i]>>(m-k))<<endl;
			k--;
		}
	//	cout<<k<<endl;
		ans-=k;
		int temp=0;
		for (int j=0;j<=m;j++) temp=max(temp,f[a[i]>>j][m-j]+m-j);
		for (int j=0;j<=m;j++) f[a[i-1]&bin[j]][j]=max(f[a[i-1]&bin[j]][j],temp-k);
	}
	int temp=0;
	for (int j=0;j<=m;j++)
		for (int i=0;i<=bin[j];i++) temp=max(temp,f[i][j]);
	cout<<ans-temp;
	return 0;
}

HDU 1075(What Are You Talking About-Trie的插入和查找)

What Are You Talking About

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/204800 K (Java/Others)
Total Submission(s): 8571    Accepted Submission(s): 2696



Problem Description
Ignatius is so lucky that he met a Martian yesterday. But he didn't know the language the Martians use. The Martian gives him a history book of Mars and a dictionary when it leaves. Now Ignatius want to translate the history book into English. Can you help
him?
 


Input
The problem has only one test case, the test case consists of two parts, the dictionary part and the book part. The dictionary part starts with a single line contains a string "START", this string should be ignored, then some lines follow, each line contains
two strings, the first one is a word in English, the second one is the corresponding word in Martian's language. A line with a single string "END" indicates the end of the directory part, and this string should be ignored. The book part starts with a single
line contains a string "START", this string should be ignored, then an article written in Martian's language. You should translate the article into English with the dictionary. If you find the word in the dictionary you should translate it and write the new
word into your translation, if you can't find the word in the dictionary you do not have to translate it, and just copy the old word to your translation. Space(' '), tab('t'), enter('n') and all the punctuation should not be translated. A line with a single
string "END" indicates the end of the book part, and that's also the end of the input. All the words are in the lowercase, and each word will contain at most 10 characters, and each line will contain at most 3000 characters.
 


Output
In this problem, you have to output the translation of the history book.
 


Sample Input
START from fiwo hello difh mars riwosf earth fnnvk like fiiwj END START difh, i'm fiwo riwosf. i fiiwj fnnvk! END
 


Sample Output
hello, i'm from mars. i like earth!
Hint
Huge input, scanf is recommended.
 


Author
Ignatius.L
 

Trie的插入和查找

话说也不难啊……

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<functional>
#include<cstdlib>
#include<iostream>
#include<cctype>
using namespace std;
#define MAXWordlen (10+10)
#define MAXLen (3000+10)
char s[MAXLen];
char word[MAXWordlen],word1[MAXWordlen];
struct node
{
	node *next[26+1];
	char t[MAXWordlen];
	node()
	{
		t[0]=0;
		for (int i=0;i<=26;i++) next[i]=NULL;
	}
}root;
void insert(char *str,char str1[])
{
	int len=strlen(str);
	node *p=&root;
	for (int i=0;i<len;i++)
	{
		if (p->next[str[i]-'a']==NULL) p->next[str[i]-'a']=new node();
		p=p->next[str[i]-'a'];
	}
	strcpy(p->t,str1);
}
node* find(int l,int r)
{
	node *p=&root;
	for (int i=l;i<=r;i++)
	{
		if (p->next[s[i]-'a']==NULL) return 0;
		p=p->next[s[i]-'a'];
	}
	if (strcmp(p->t,"")) return p;
	return 0;
}
int main()
{
	scanf("START");
	while (scanf("%s",word)&&strcmp(word,"END"))
	{
		scanf("%s",word1);
		insert(word1,word);
	}
	gets(word);
	scanf("START");
	gets(word);
	while (gets(s))
	{
		if (!strcmp(s,"END")) break;
		int len=strlen(s);
		s[len+1]=0;
		for (int i=0;i<len;i++)
		{
			if (islower(s[i]))
			{
				int j=i;
				while (islower(s[j+1])) j++;
				node *p=find(i,j);
				if (p) printf("%s",p->t);
				else
				{
					for (int k=i;k<=j;k++) printf("%c",s[k]);
				}
				i=j;
			}
			else printf("%c",s[i]);
		}
		printf("n");
	}
	return 0;
}