[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状态时要充分考虑哪些状态是真正需要用到的。