A. 少废话,你___多少?
- 因为次大值最大值“必有一战”得到的\(2z_{m-1}>z_m\)则无解的判据并不是无关紧要的特例,而是后续解题的一个很重要的先决条件
- \((l[i],i)\)之间的数和\(i\)一起处理不管怎么样都很讨厌,类似“费用提前计算”的尝试都无果而终,于是可以尝试“费用延后计算”,当前只考虑\(i\)自己和\([1,l[i]]\)区间数的方案,而且一旦下一个最大值出现,\((l[i],i)\)就好处理了
- 如果忽略掉数与数之间的空位,转移起来就很麻烦,于是考虑填数的方法,当前的方案要给未来留下余地。这样,\(i\)就只能填在当前的第1个空位中,转移方程水到渠成
#include <bits/stdc++.h>
using namespace std;
int a[1000005],l[1000005];
long long f[1000005],g[1000005];
const int mod=998244353;
int power(int n,int p)
{
if(p==0)
{
return 1;
}
long long tmp=power(n,p/2);
if(p%2==1)
{
return tmp*tmp%mod*n%mod;
}
return tmp*tmp%mod;
}
long long jc[1000005],jcinv[1000005];
void pre()
{
jc[0]=1;
for(int i=1;i<=1000000;i++)
{
jc[i]=jc[i-1]*i%mod;
}
jcinv[1000000]=power(jc[1000000],998244351);
for(int i=1000000-1;i>=0;i--)
{
jcinv[i]=jcinv[i+1]*(i+1)%mod;
}
}
long long A(int n,int m)
{
return jc[n]*jcinv[n-m]%mod;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
pre();
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
if(2*a[n-1]>a[n])
{
cout<<0<<endl;
return 0;
}
int p=0;
for(int i=1;i<=n;i++)
{
while(p+1<i&&2*a[p+1]<=a[i])
{
p++;
}
l[i]=p;
}
for(int i=1;i<=n;i++)
{
f[i]=g[l[i]]*jcinv[n-l[i]-1]%mod;
f[i]=(f[i]+A(n-1,l[i]))%mod;
g[i]=(g[i-1]+f[i]*jc[n-l[i]-2]%mod)%mod;
}
cout<<f[n]<<endl;
return 0;
}
B. shoot the tank!!
- 典型的完全背包模型,有趣的是也可以用图论方法解决,只要把血量状态定义为节点就好了。因为dijkstra算法的时间复杂度只和n有关,所以这样做显然也是正确的
#include <bits/stdc++.h>
using namespace std;
int a[1005],b[1005],f[10005];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int h,n;
cin>>h>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i];
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=h;j++)
{
f[j]=min(f[j],f[max(j-a[i],0)]+b[i]);
}
}
cout<<f[h]<<endl;
return 0;
}
D. 超进化:冒泡排序
- 【观察性质】
- 冒泡排序的性质:
- 经过 i 次扫描后,数列的末尾 i 项必然是最大的 i 项
- 每⼀轮,每个位置的值最多往前移动⼀位
- 因⽽,冒泡排序k轮以后,第1个位置必定是前k+1个位置中最⼩的,第2个位置必定是前k+2个位置中,除去第1个位置现在的数字外,最⼩的,后续同理。可以用堆维护
- 【分析过程】
- 不断讨论细节会把自己弄晕的!拒绝晕倒,尝试使用数学符号
- 一轮排序后的序列状态是:
- \(min(a_1,a_2),min(max(a_1,a_2),a_3),min(max(a_1,a_2,a_3),a4),...,max(a_1,a_2,...,a_n)\)
- 两轮排序后的序列状态是:
- \(min(a_1,a_2,a_3),min(max(min(a_1,a_2),min(max(a_1,a_2),a_3)),min(max(a_1,a_2,a_3),a4))\)
- 分析\(min(max(min(a_1,a_2),min(max(a_1,a_2),a_3)),min(max(a_1,a_2,a_3),a4))\)
- =\(min(rank_2(a1,a2,a3),min(max(a_1,a_2,a_3),a4))\) 【不妨设\(a_1<a_2<a_3\)】
- =\(min(a_2,min(a_3,a_4))\)=\(min(a_2,a_3,a_4)\) 也可以分析出结果
#include <bits/stdc++.h>
using namespace std;
int a[200005],b[200005];
priority_queue<int,vector<int>,greater<int> >q;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
int T;
cin>>T;
while(T--)
{
long long k;
cin>>k;
for(int i=1;i<=k/(n-1)+1&&i<=n;i++)
{
q.push(a[i]);
}
int i=1;
while(i<=n)
{
b[i]=q.top();
q.pop();
if(i+k/(n-1)+1<=n)
{
q.push(a[i+k/(n-1)+1]);
}
i++;
}
for(int j=1;j<=k%(n-1);j++)
{
if(b[j]>b[j+1])
{
swap(b[j],b[j+1]);
}
}
for(int i=1;i<=n;i++)
{
cout<<b[i]<<" ";
}
cout<<endl;
}
return 0;
}