速度训练4 题解

文章目录


T1
T2
T3
T4
T5
T6
T7
由于时间原因,题目翻译略.

A

贪心将给定的二元组按照 a a a从大到小排序,显然当某个 a 1 a1 a1值在集合里为最大的时候最好的办法就是贪心把所有 a a a值小于 a 1 a1 a1的二元组全部插入与 a 1 a1 a1相同的集合.
所以枚举所有 a 1 a1 a1,答案即为 a 1 + m a x b a1+maxb a1+maxb的最小值.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=1e5;
typedef int fuko[yuzu|10];
typedef pair<int,int> pii;
pii a[yuzu|10];
int main(){
for (int t=read(),i,zxy=0;t--;){
  int n=read(),llx=2e9,maxb=0;
  for (i=1;i<=n;++i) a[i]=pii(read(),read());
  sort(a+1,a+n+1,greater<pii>());
  a[n+1].first=a[0].second=0;
  for (i=1;i<=n;++i) maxb=max(maxb,a[i-1].second),llx=min(llx,a[i].first+maxb);
  /*maxb是目前的b值的最大值,llx是a+b的最小值.*/
  printf("Case %d: %d\n",++zxy,llx);
  }
}

B

题目相当于先将所有数向下取整,再将 n n n个数变为向上取整.
d i f ( x ) dif(x) dif(x)为一个数向上取整的值减向下取整的值.
显然 d i f ( x ) dif(x) dif(x) x x x为整数时取 0 0 0,否则取 1 1 1.
我们将 a [ i ] a[i] a[i] d i f dif dif值排序,算出 a [ i ] − f l o o r ( a [ i ] ) a[i]-floor(a[i]) a[i]floor(a[i])值的和 s u m sum sum.
那么我们接下来可以贪心,如果 s u m > 0.5 sum>0.5 sum>0.5我们就给它 − 1 -1 1,否则不变.
因为一个 a [ i ] a[i] a[i]从向下取整变为向上取整,会带来 − 1 -1 1的贡献,由于要让绝对值最小,所以当 s u m < 0 sum<0 sum<0时就不要动它了.
最后坐等答案出来即可.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
typedef double db;
const int aoi=4038;
typedef db kotori[aoi];
kotori a,f,c,dif;
db sum;
int main(){
int i,n=read()<<1;
for (i=1;i<=n;++i){
  scanf("%lf",&a[i]);
  f[i]=floor(a[i]);
  c[i]=ceil(a[i]);
  dif[i]=c[i]-f[i];
  sum+=c[i]-a[i];
  }
int l=1,r=n;
sort(dif+1,dif+n+1);
for (i=1;i<=n>>1;++i){
  sum-=dif[sum>0.5?r--:l++];
  }printf("%.3lf",abs(sum));
}

C

高妙的动态规划.
接下来首先欣赏xhkxhk大佬暴力压行代码.

#include<bits/stdc++.h>
int i,j=1,a[5010],f[5010][5010];
main(){
	for(std::cin>>i;j<=i;j++)std::cin>>a[j];
	for(;i;i--)for(j=0;j<i;j++)f[i-1][j]=a[j]<a[i]?std::min(f[i][j]+1,f[i][i]+a[i]-a[j]):f[i][i];
	std::cout<<**f;
}

然后介绍介绍标算.
很明显答案不能大于 n n n(我一列一列涂肯定能够刚好涂完的吧.)
然后我们考虑横着涂.
我们考虑区间 [ l , r ] [l,r] [l,r],找出区间 l , r l,r l,r内最小数的位置 m m m.
然后我们把 [ l , r ] [l,r] [l,r]的最下方涂 a [ m ] a[m] a[m]次,这样要涂的部分转化为 [ l , m − 1 ] 和 [ m + 1 , r ] [l,m-1]和[m+1,r] [l,m1][m+1,r]两个部分.
而两个部分的高度都下降了 a [ m ] a[m] a[m],就能够分治解决问题.
这样就可以写出dp方程了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int aoi=5038; /*葵怎么就这么萌啊~*/
int a[aoi];
int dfs(int l,int r,int h){
if (l>r) return 0;
int m=min_element(a+l,a+r+1)-a;
return min(r-l+1,dfs(l,m-1,a[m])+dfs(m+1,r,a[m])+a[m]-h);
/*[l,r]区间涂色的最小次数是r-l+1次.*/
}
int main(){
int i,n=read();
for (i=1;i<=n;++i) a[i]=read();
write(dfs(1,n,0));
}

D

首先预处理组合数逆元.
我们对 x x x求一波前缀和,然后对 y y y的情况进行 d p dp dp.
dp[i][j]表示当前考虑到第 i i i个教室,还有 j j j人没有分配的方法总数.
枚举当前有 k k k人分配到前 i i i个教室( k k k不能超过 y y y),转移方程为
dp[i][j]=(dp[i][j]+dp[i-1][j-k]*zuhe(x[i]-j+k,k)%mod)%mod.
最后用 d p [ m ] [ x [ m ] ] dp[m][x[m]] dp[m][x[m]]乘从没有选择的学生中取 x [ i ] x[i] x[i]个学生出来的取法总数即可.
数组开小了wa了一发.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int mod=1e9+7,_=150;
ll dp[_][9999],jic[1098]={1},inv[1098];
int x[_],y[_];

ll kasumi(ll a,ll b=mod-2){
ll s=1;
for (;b;b>>=1,a=a*a%mod) if (b&1) s=s*a%mod;
return s;
}

ll zuhe(int n,int m){
return jic[n]*inv[m]%mod*inv[n-m]%mod;
}

int main(){
int i,j,k,m=read();
for (i=1;i<1098;++i) jic[i]=jic[i-1]*i%mod;
inv[1097]=kasumi(jic[1097]);
for (i=1096;~i;--i) inv[i]=inv[i+1]*(i+1)%mod; 
for (i=1;i<=m;++i) x[i]=read()+x[i-1];
for (i=1;i<=m;++i) y[i]=read();
memset(dp,0,sizeof dp);
for (**dp=i=1;i<=m;++i){
  for (j=0;j<=x[i];++j){
    for (k=0;k<=min(j,y[i]);++k){
      dp[i][j]=(dp[i][j]+dp[i-1][j-k]*zuhe(x[i]-j+k,k)%mod)%mod;
	  }
	}
  }
ll ans=dp[m][x[m]];
for (i=1;i<=m;++i) ans=ans*zuhe(x[m]-x[i-1],x[i]-x[i-1])%mod;
write(ans);
}

E

我们推一下公式.
假设要求不严格单调递增的序列.不严格单调递减同理,减去所有数都一样的 n n n种情况即可.
令构造出的序列为 a a a,下标是 1 → n 1\to n 1n.我们在 a a a的左边加一个 1 1 1,右边加一个 n n n.
构造 b [ i ] = a [ i ] − a [ i − 1 ] b[i]=a[i]-a[i-1] b[i]=a[i]a[i1],则 b b b序列有 n + 1 n+1 n+1项,并且 b b b序列的所有数之和为 n − 1 n-1 n1.
这种题看起来很像一个隔板法. b 1 + b 2 + . . . + b n + 1 = n − 1 b_{1}+b_{2}+...+b_{n+1}=n-1 b1+b2+...+bn+1=n1.
由于 b b b序列可以为 0 0 0,我们给两边同时加 n + 1 n+1 n+1.
那么就变成了在 2 × n − 1 2\times n -1 2×n1个空里插 n n n个板的问题了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=2e5,mod=1e9+7;
typedef ll fuko[yuzu|10];
fuko jic={1},inv;

ll kasumi(ll a,ll b=mod-2){
ll s=1;
for (;b;b>>=1,a=a*a%mod) if (b&1) s=s*a%mod;
return s;
}

ll zuhe(int n,int m){
return jic[n]*inv[n-m]%mod*inv[m]%mod;
}

int main(){
int i,n=read();
for (i=1;i<=yuzu;++i) jic[i]=jic[i-1]*i%mod;
for (inv[yuzu]=kasumi(jic[yuzu]),i=yuzu-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
write((zuhe(2*n-1,n)*2%mod-n+mod)%mod);
}

F

d p dp dp来解决.
首先预处理组合数.
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示目前处理到第 i i i个房间,还有 j j j个人,最长队伍是 k k k的概率.
枚举当前房间进了 p p p个人,概率是从剩下的人中选择了 p p p个人,并且他们都进了这个房间的概率.
dp[i][j-p][max(l,k)]+=dp[i-1][j][k]*c[j][p]*pow(1.0/m,p);
最后用 ∑ i = 1 n d p [ m ] [ 0 ] [ i ] × i \sum_{i=1}^{n}dp[m][0][i]\times i i=1ndp[m][0][i]×i求得答案.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
typedef double db;
const int _=55;
db c[_][_],dp[_][_][_];
int a[_];
int main(){
int i,j,k,p;
for (i=0;i<=_;++i){
  for (j=0;j<=i;++j){
    c[i][j]=!j?1:c[i-1][j-1]+c[i-1][j];
    }
  }
int n=read(),m=read();
for (i=1;i<=m;++i) a[i]=read();
db llx=0;
memset(dp,0,sizeof dp);
dp[0][n][0]=1;
for (i=1;i<=m;++i){
  for (j=0;j<=n;++j){
    for (p=0;p<=j;++p){
      for (k=0;k<=n;++k){
        int l=ceil(p*1.0/a[i]);
        dp[i][j-p][l>k?l:k]+=dp[i-1][j][k]*c[j][p]*pow(1.0/m,p);
	    }
      }
    }
  } 
for (i=1;i<=n;++i) llx+=dp[m][0][i]*i;
printf("%.18lf",llx);
}

G

把这些区间离散化后差分,算每一个最小区间被包括了几次.
如果被包括的次数 x ≥ k x\geq k xk,答案即加上长度乘上 C ( x , k ) C(x,k) C(x,k)的值.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) x=-x,pc('-');
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=4e5,mod=1e9+7;
typedef ll fuko[yuzu|10];
fuko sum,jic={1},inv,l,r,li;
ll kasumi(ll a,ll b=mod-2){
ll s=1;
for (;b;b>>=1,a=a*a%mod) if (b&1) s=s*a%mod;
return s;
}
ll zuhe(int n,int m){
return jic[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
int cnt=0,i,n=read(),k=read();
for (i=1;i<=yuzu;++i) jic[i]=jic[i-1]*i%mod;
inv[yuzu]=kasumi(jic[yuzu]);
for (i=yuzu-1;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
for (i=1;i<=n;++i){
  l[i]=read(),r[i]=read();
  li[cnt++]=l[i],li[cnt++]=++r[i];
  }
sort(li,li+cnt);
cnt=unique(li,li+cnt)-li;
#define lb lower_bound
for (i=1;i<=n;++i){
  ++sum[lb(li,li+cnt,l[i])-li];
  --sum[lb(li,li+cnt,r[i])-li];
  }
ll llx=0,nk=sum[0];
for (i=1;i<=cnt;++i){
  if (nk>=k) llx=(llx+zuhe(nk,k)*(li[i]-li[i-1])%mod)%mod; nk+=sum[i]; 
  }write(llx);
}

谢谢大家.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
06-01
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值