[1018NOIP模拟赛]

题目描述 Description###

精灵王国要同侵略 $ Bzeroth $ 大陆的地灾军团作战了。
众所周知,精灵王国有 \(N\) 座美丽的城市,它们以一个环形排列在$ Bzeroth$ 的大陆上。为了补充军需,王国财政部决定从这$ N $ 座城市的商人们腰包中征用物资,其中第$ i $ 座城市的商人们共可以提供 $ A[i] $ 单位的物资。当征粮队征集物资的时候,他们发现一件出乎意料的事情:如果他们征集了第 \(i\) 座城市的物资,那么与第 $i $座城市相邻的两座城市($1 $ 号城市与 \(N\) 号城市相邻)里的商人都会逃跑,即征粮队不能再到这两座城市征集物资。财政部得知这一消息后迅速召开了会议,决定让精灵王国最好的工程师——你,来设计出最优的征集方案,使得联军能够征集到的物资尽可能多。

输入描述 Input Description###

第一行一个正整数$ N $ 。
第二行$ N $ 个正整数,第 i 个数为$ A[i] $ 。

输出描述 Output Description###

输出最优情况下征集到的物资总和。

样例输入 Sample Input###

5 2 3 4 5 5

样例输出 Sample Output###

9

数据范围及提示 Data Size & Hint###

测试数据编号 数据范围 其他限制
1 - 5 2 ≤ N ≤ 20
6 - 7 2 ≤ N ≤ 2501
8 - 9 2 ≤ N ≤ 152501 所有 $ A[i] $均相等
10 2 ≤ N ≤ 152501
对于 100%的数据:$1 ≤ A[i] ≤ 10^9 $ 。

之前的一些废话###

没有废话

题解###

环形灭虫子,先考虑直线情况, $ dp[i][0] $ 表示第i个不选的最大收益,$ dp[i][1] $ 表示第i个选的最大收益,转移的话是\(dp_{i,1}=max\texttt{\{}0,dp_{i-1,0} \texttt{\}}+a_i\),\(dp_{i,0}=max\texttt{\{}dp_{i-1,1},dp_{i-1,0} \texttt{\}};\)
若是环形的话,则强行令端头选,端头不选各DP一次。

代码###

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<int,int> PII;
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=152510,oo=2147483647;
int n,a[maxn];
LL dp[maxn][2],ans;
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	dp[1][1]=a[1];dp[1][0]=-oo;
	for(int i=2;i<=n;i++)
	{
		dp[i][1]=max(0ll,dp[i-1][0])+a[i];
		dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
	}
	ans=dp[n][0];
	dp[1][1]=-oo;dp[1][0]=0;
	for(int i=2;i<=n;i++)
	{
		dp[i][1]=max(0ll,dp[i-1][0])+a[i];
		dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
	}
	ans=max(ans,max(dp[n][0],dp[n][1]));
	printf("%lld\n",ans);
	return 0;
}

总结###

没有总结

题目描述 Description###

每一个生活在$ Bzeroth $ 大陆的精灵都知道,若想毁灭他们深爱的这片土地,必须要先摧毁守护 $ Bzeroth $ 大陆的$ N $ 个能量核心。
这 $ N $ 个能量核心由诸神修建的 $ M $ 条秩序神链沟通,其中第$ i $ 条秩序神链沟通了第$ u_i $ 和第 $ v_i $个核心,稳定程度为 $ w_i$ 。最开始,这 $ N $ 个能量核心是连通的(即任意两个能量核心均能通过神链直接或间接沟通)。地灾军团的军师黑袍得知,他们可以摧毁若干神链使得这 $ N $ 个能量核心 恰好被分成两个连通块,从而削弱 $ Bzeroth $ 大陆的防卫能量。由于被摧毁后的防卫能量等于未被摧毁的神链的稳定程度之和,所以他找到了精灵王国最好的工程师,同时也是地灾军团潜伏在 $ Bzeroth $ 大陆多年的间谍——你,来设计出最优的方案,使得未被摧毁的神链的稳定程度之和最小。

输入描述 Input Description###

第一行为 2 个整数 $ N $ 、$ M $ 。
接下来 M 行每行三个正整数 $ u_i $ 、$ v_i $ 、 $ w_i $ 。

输出描述 Output Description###

输出最优情况下未被摧毁的神链的稳定程度之和。

样例输入 Sample Input###

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

样例输出 Sample Output###

6

数据范围及提示 Data Size & Hint###

测试数据编号 数据范围 其他限制
1 - 5 2 ≤ N、M ≤ 15
6 - 7 2 ≤ N、M ≤ 152501 2 ≤ N ≤ 15
8 2 ≤ N、M ≤ 152501 M = N - 1
9 2 ≤ N、M ≤ 152501 M = N
10 2 ≤ N、M ≤ 152501
对于 100%的数据:$ 1 ≤ u_i、v_i≤ N,1 ≤ w_i ≤ 10^9 $ 。

之前的一些废话###

没有废话

题解###

随便试一两个图就发现答案是原图的最小生成树-最小生成树上的最大权值边

代码###

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<int,int> PII;
typedef pair<int,PII> PIP; 
#define X first
#define Y second
#define MP make_pair
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=152510,oo=2147483647;
int n,m,a,b,c,f[maxn];
LL sum,MAX;
PIP E[maxn];
int getf(int x){return f[x]==x ? x : f[x]=getf(f[x]);}
void merge(int a,int b)
{
	int x=getf(a),y=getf(b);
	if(x!=y)f[x]=y;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++)a=read(),b=read(),c=read(),E[i]=MP(c,MP(a,b));
	sort(E+1,E+m+1);
	for(int i=1;i<=m;i++)
	{
		int u=E[i].Y.X,v=E[i].Y.Y;
		if(getf(u)!=getf(v))
		{
			sum+=E[i].X;
			MAX=max(MAX,(LL)E[i].X);
			merge(u,v);
		}
	}
	printf("%lld\n",sum-MAX);
	return 0;
}

总结###

没有总结

题目描述 Description###

精灵心目中亘古永恒的能量核心崩溃的那一刻,$ Bzeroth $ 大陆的每个精灵都明白,他们的家园已经到了最后的时刻。
就在这危难关头,诸神天降神谕,传下最终兵器——潘少拉魔盒。然而当精灵们准备打开魔盒时,魔盒的守护灵出现在精灵们面前:“如果你们想要拯救世界,必须要先解决这个困难的问题:定义一个 N 阶数列 A 为神奇数列当且仅当对所有$ 2 ≤i ≤ N - 1 $ ,都有 $ A_i-1 + A_i+1 ≥ 2 × A_i $ 。现在有一个$ N $ 阶正整数列$ B $ ,请计算将 $ B $ 数列均匀随机打乱之后,得到的数列是神奇数列的概率 $ P $ 。你只需要输出 $ P × (N!) mod 998244353 $ 的结果即可。(显然 $ P × (N!) $ 一定是个整数)。”

输入描述 Input Description###

第一行为 1 个正整数 $ N $。
第二行为 N 个正整数 $ A_i $。

输出描述 Output Description###

输出 $ P × (N!) mod 998244353 $ 的结果。

样例输入 Sample Input###

4 1 2 1 3

样例输出 Sample Output###

8

数据范围及提示 Data Size & Hint###

对于 50%的数据:3 ≤ N ≤ 10。
对于 80%的数据:3 ≤ N ≤ 20。
对于 100%的数据:3 ≤ N ≤ 40,$ 1 ≤A_i ≤ 10^9 $ 。

之前的一些废话###

没有废话

题解###

发现当且仅当A数组是一个下凸函数时才满足题意
考试时候写的是80分,排个序是肯定的,然后我们确定了最小值,开始往两边依次递增的填数,用 $ 2^n $ 枚举在左边的数,然后判一判是否合法。最后算出总方案数乘以最小数个数的阶乘即可。正解在这基础上稍微改一下即可,往两边插值的时候判断能否插入只与左右两端最大值次大值有关,设计状态 $ dp[i][j][k][l] $ 表示当前序列最左边两个数分别是$ a_i $ 和$ a_j $ ,最右边两个数分别是$ a_k $ 和$ a_l $ 的合法序列数,转移显然。复杂度 \(O(n^4)\)

代码###

#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long LL;
#define mem(a,b) memset(a,b,sizeof(a))
typedef pair<int,int> PII;
typedef pair<int,PII> PIP;
const int MOD=998244353,maxn=42;
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
int n,a[maxn],sum=1,jc[maxn],dp[maxn][maxn][maxn][maxn],ans;
int main()
{
    n=read(); 
    jc[0]=1;
    for(int i=1;i<=n;i++)a[i]=read(),jc[i]=((LL)jc[i-1]*(LL)i)%MOD;
    sort(a+1,a+n+1);
    for(int i=2;i<=n;i++)
	{
		if(a[i]!=a[i-1])break;
		sum++;
	}
	for(int i=sum+1;i<=n;i++)a[i-sum+1]=a[i];
	dp[1][0][1][0]=1;
	n=n-sum+1;
	for(int i=1;i<=n;i++)
	    for(int j=0;j<i;j++)
		    for(int k=1;k<=n;k++)
			    for(int l=0;l<k;l++) 
			    {
			    	int pos=max(i,k)+1;
			    	if(pos==n+1){ans=(ans+dp[i][j][k][l])%MOD;continue;}
			    	if(2*a[i]<=a[pos]+a[j] || i==1)dp[pos][i][k][l]=(dp[pos][i][k][l]+dp[i][j][k][l])%MOD;
			    	if(2*a[k]<=a[pos]+a[l] || k==1)dp[i][j][pos][k]=(dp[i][j][pos][k]+dp[i][j][k][l])%MOD;
			    }
	printf("%d\n",((LL)ans*(LL)jc[sum])%MOD);		    
	return 0;
}

总结###

设计DP状态时要充分考虑哪些状态是真正需要用到的。

posted @ 2017-10-19 16:05  小飞淙的云端  阅读(507)  评论(0编辑  收藏  举报