JMS NO.1 SCHOOL TEXT DAY 2

JMS NO.1 SCHOOL TEXT DAY 2



Problem.A

一个字符串被称为括号序列当且仅当该字符串中不包含除“(”和“)”之外的任何字符。如果可以通过在此序列中插入字符“+”“1”来获得正确的算术表达式,则称该括号序列为一个匹配的括号序列(简称RBS)。例如,“”、“(())”和“()()”RBS,而“)(”和”(()”不是RBS

RBS中的每个左括号都与某个右括号成对,因此我们可以将RBS的嵌套深度定义为最大括号对数,这样第二对位于第一对中,第三对位于第二对中,以此类推。例如,“”的嵌套深度为0“()()()”1“()((())())”3

现在,你得到了长度为偶数nRBS。你应该将S的每个括号着色为两种颜色之一:红色或蓝色。括号序列R(仅由红色括号组成)为RBS,括号序列B(仅由蓝色括号组成)应为RBS。其中任何一个都可以是空的。不允许对SRB中的字符重新排序。括号不能不着色。

在所有可能的方案中,你应该选择一个最小化RB的最大嵌套深度的方案。输出这个嵌套深度。

Input

第一行包含一个整数n2n2×105)代表RBS s的长度。

第二行包含一个匹配的括号序列s

 

 Output

输出一行一个整数代表所求嵌套深度。

 

Example

input

2
()

output

1

 

input

4

(())

output

1

 

input

10

((()())())

output

2

 

Note

在第一个例子中,一种合理的方案:RR

在第一个例子中,一种合理的方案:RBRB

在第一个例子中,一种合理的方案:RBBRRRBBBB

 

Hint

对于30%的数据,n15

对于另外20%的数据,保证序列中“(”不会在“)”后出现





标程:
#include<bits/stdc++.h>
using namespace std;
int n,red,blue,ans;
string s;
int main(){
	scanf("%d",&n);
	cin>>s;
	for(int i=0;i<n;++i){
		if(s[i]==')'){
			if(red>blue) --red;
			else --blue;
		}	
		else{
			if(red<blue) ++red,ans=max(ans,red);
			else ++blue,ans=max(ans,blue);
		}
	}
	printf("%d",ans);
	return 0;
}


30pts:2^n枚举染色方案,判断嵌套深度。 另外20pts:序列形如“(((((((((((((((((((())))))))))))))))))))”,所以输出((n/2)+1)/2即可。 100pts:用两个数分别记录红、蓝当前的嵌套深度,出现新的一层时,当前的嵌套深度哪个小,就把新的一层染成什么颜色。时间复杂度O(n)。

Problem.B


你在一棵由n个点组成的树(一个无向连通的无环图)上玩游戏。

起初,所有点都是白色的。游戏的第一步,你选择一个点并把它染成黑色。然后,接下来的每一步中,选择一个与某个黑色点相邻的白色点,并将其染为黑色。

每次选择点时,都会获得与 所选点联通的白色点的数目(包括所选点) 相等的分数。游戏结束时,所有的点都被染成黑色。

下面是一个示例:

14已经被染黑。如果你选择点2,你会获得4分(联通的白色点有2,3,5,6.如果你选择点9,你会获得3分(联通的白色点有7,8,9)。

你的任务是使得分最大。

Input

第一行包含一个整数n2n2×105)代表树包含的点数。

 

接下来n-1行每行描述了树的一条边。第i行两个整数uivi代表uivi之间有边相连

1ui,vinuivi)。

 

 Output

输出一行一个整数,你能得到的最大得分。

 

Example

input

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

output

36

 

input

5
1 2
1 3
2 4
2 5

output

14

 

Note

第一个例子中,树的形态如题目描述所示。

 

Hint

对于30%的数据,n2,000

对于另外20%的数据,保证原图是一条链





标程:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ans;
int n;
int sz[200004];
struct edge{
	int to,nt;
}e[400004];
int ne,h[200004];
void add(int u,int v){
	e[++ne].to=v;e[ne].nt=h[u];h[u]=ne;
}
void dfs(int x,int fa){
	sz[x]=1;
	for(int i=h[x];i;i=e[i].nt){
    	if(e[i].to==fa)continue;
    	dfs(e[i].to,x);
    	sz[x]+=sz[e[i].to];
}
ans+=sz[x];
}
void calc(int x,int fa,ll now){
	ans=max(ans,now);
	for(int i=h[x];i;i=e[i].nt){
    	if(e[i].to==fa)continue;
    	calc(e[i].to,x,now+n-2LL*sz[e[i].to]);
}
}
int main(){
	memset(h,0,sizeof(h));
	ne=0;
	scanf("%d",&n);
	for(int i=1;i<n;i++){
    	int u,v;scanf("%d%d",&u,&v);
    	add(u,v);add(v,u);
}
dfs(1,0);
calc(1,0,ans);
printf("%lld\n",ans);
return 0;
}


可以发现,如果染色的第一个点确定,则最终会获得的分数也是确定的。即在根节点确定的前提下,只需要在树上dfs,在每个节点处把该点为根的子树大小加入答案即可。 30pts:O(n)枚举树的根节点,再dfs遍历O(n)计算答案,时间复杂度O(n2)。 另外20pts:得分即为1+2+3+…+n。 100pts:在dfs求出以某一节点为根后,再次使用dfs进行换根操作。如果以u为根的答案已知为ans,则如果以u的某一子节点v为根,答案即为ans+n-2*size[v]。

Problem.C


公路沿线有n个城市,可以用一条直线表示。第i个城市位于距起点ai公里处。所有的城市都位于同一个方向。有m辆卡车从一个城市驶向另一个城市。

每辆卡车可以用4个整数来描述:起始城市si、结束城市fi、油耗ci和允许的加油次数ri。第i辆卡车每公里耗油量为ci升。

当卡车到达某个城市时,它可以加油。第i辆卡车最多可以加油ri次。每次加油都会把卡车的油箱加满。一开始,所有卡车的油箱都是满的。

所有卡车都将配备相同的V升油箱。请找出最小可能的V,使所有卡车都能在加油不超过规定次数的前提下到达目的地。

Input

第一行包含两个整数nm2n4001m250,000)代表城市数和卡车数。

第二行包含n个整数a1,a2,…an1ai109aiai+1)升序表示城市的位置。

接下来m行,每行4个整数。第i行包含整数si,fi,ci,ri (1≤si<fi≤n, 1≤ci≤109, 0≤ri≤n)描述了第i辆卡车。

 Output

输出一行一个整数表示最小可能的油箱大小V,满足所有的卡车都能到达目的地。

Example

input

7 6

2 5 7 10 14 15 17

1 3 10 0

1 7 12 7

4 5 13 3

4 7 10 1

4 7 10 1

1 5 11 2

output

55

 

Note

我们从细节考虑问题:

1.     第一辆卡车需要在不加油的条件下从2到达7,所以它需要的油箱大小最小是50

2.     第二辆卡车需要从2到达17,且在任意城市都可以加油,所以它需要的油箱大小最小是48

3.     第三辆卡车需要从10到达14,且在此之间没有任何城市,所以它需要的油箱大小最小是52

4.     第四辆卡车需要从10到达17且只能加油一次。最优的方案是在城市5(位置14)加油,所以它需要的油箱大小最小是40

5.     第五辆卡车情况与第四辆相同,所以它需要的油箱大小最小是40

6.     第四辆卡车需要从2到达14且只能加油两次。第一次在城市23,第二次在城市4,所以它需要的油箱大小最小是40

 

Hint

对于30%的数据,满足n80

对于另外30%的数据,满足m10,000





标程:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int maxn=402;
int n,m;ll ans;
int dp[maxn][maxn][maxn],a[maxn];
int s,f,r;ll c;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
    scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
    for(int j=i;j<=n;j++){
        dp[i][j][0]=a[j]-a[i];
    }
}
for(int k=1;k<=n;k++){
    for(int i=1;i<=n;i++){
        int w=i;
        for(int j=i;j<=n;j++){
            while(w<j&&max(dp[i][w][k-1],a[j]-a[w])>max(dp[i][w+1][k-1],a[j]-a[w+1])) ++w;
            dp[i][j][k]=max(dp[i][w][k-1],a[j]-a[w]);
        }
    }
}
while(m--){
    scanf("%d%d%lld%d",&s,&f,&c,&r);
    ans=max(ans,dp[s][f][r]*c);
}
printf("%lld\n",ans);
return 0; 
}


30pts:设 dp{i,j,k}为:从第 i 个城市到第 j 个城市分成 k 段,这 k 段中长度最大的一段的最小值 状态转移方程:

dp {i,j,k}=min{ max(dp{i,w,k-1},aj-aw) } (0<k≤n)(i≤w≤j )
目标:max{ ci*dp{si,fi,ri}} i<=m
O(n^4)dp求解即可。
另外30pts:二分答案v,O(nm)判断是否可行。时间复杂度O(nmlog)
100pts:
考虑30分dp算法中有两个结果:
  1)当 k,i 确定, j 在不断向右移时,对每个 j 取到的 w 具有单调性。
  2)同时,当 j 确定时,不同的 w 对应的取值呈“先减后增” 的趋势。
  我们先证明第二点,当i j k 确定时,转移方程中 我们设dp{i,w,k-1}为Aw,aj-aw为Bw,当w变大时,Aw可能变大,Bw必定变小,所以取值一开始肯定是取Bw的,慢慢变的有可能取Aw,可以想象,这个dp方程一开始肯定是w越大越好,但是当某一个临界点,如果比前面大了,那我们会发现,此时的最大值必定不是Bw,因为Bw<B(w-1),这是必定的。所以最大值是Aw,而w越大,Aw则可能变大,但绝不变小,所以不会有变小的趋势了,证毕。
  然后证明第一点,假设j‘=j+1,我们先看w是否会前移。发现j变成j‘后,只有Bw会变大,Aw是不变的,如果w向前移动,则Bw变大,Aw可能变小,即此时移动一定是为了选取Aw的值,那么在j点也一定取了Aw的值,即若在j点w’取w-1会更优。所以不会前移。
  那么看w是否会后移,由于Bw会变大,Aw只是可能变大,后面的项比前面的项更有可能用到Aw,所以后面的项可能更优,w可能后移,有单调性,证毕。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值