A. Eshag Loves Big Arrays(贪心)
大致题意:一个数组a有n个整数,你能够对数组进行任意次数的以下操作:
从数组中挑出几个数字,求出平均数,再删除这几个数字中大于平均数的数字。
求出最多能删除多少个数字?
典型的贪心,既然要删除大于我们挑出数字平均数的数字,而且要多删除几个,那么我们知道平均数是在最大值和最小值之间的,要尽量地多删除数字,就需要平均数尽量地小,那就干脆挑出最小一个的数字,去删除比它大的数字就行了,原理是:挑出两个数字,一定会删除大的那个数字。
#include <bits/stdc++.h>
using namespace std;
int a[110];
int main(void)
{
int t;
cin>>t;
while(t--)
{
int minn=110,ans=0,n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
minn=min(minn,a[i]);
}
for(int i=1;i<=n;i++)
{
if(a[i]>minn)
ans++;
}
cout<<ans<<endl;
}
return 0;
}
B. Sifid and Strange Subsequences
题目大意:strange数组:长度为n,
M
A
X
MAX
MAX为其数组的最大值,任意两个数字
a
i
a_i
ai和
a
j
a_j
aj(1<=i<j<=n),
∣
a
i
−
a
j
∣
>
=
M
A
X
|a_i-a_j|>=MAX
∣ai−aj∣>=MAX。
接下来给一个t是这是数据组数,每一次n个数字,求这个数组中最多有多少个数字可以组成strange数组。
接下来就要利用,两者差的绝对值的性质了,
如果两个数都是正数,
∣
a
i
−
a
j
∣
<
M
A
X
|a_i-a_j|<MAX
∣ai−aj∣<MAX,相当于
∣
a
i
−
a
j
∣
|a_i-a_j|
∣ai−aj∣等于大的数减去小的数,那么strange数组不可可能有两个正数。
如果两个数小于等于零,
∣
a
i
−
a
j
∣
>
=
M
A
X
|a_i-a_j|>=MAX
∣ai−aj∣>=MAX必定成立,
∣
a
i
−
a
j
∣
>
=
0
|a_i-a_j|>=0
∣ai−aj∣>=0,
M
A
X
<
=
0
MAX<=0
MAX<=0,所以,strange数组中可以有任意个小于等于零的数。
如果数组中有一个整数,那么只需要在小于等于0的数当中,满足
∣
a
i
−
a
j
∣
>
=
M
A
X
|a_i-a_j|>=MAX
∣ai−aj∣>=MAX即可,(一正一负的情况必定成立,只需考虑小于等于0的数),即
∣
a
i
−
a
j
∣
>
=
这
个
正
数
|a_i-a_j|>=这个正数
∣ai−aj∣>=这个正数,要这个条件尽量成立,这个正数要尽量地小,我们就取最小的那个正数(这样成立的几率最大)。
算法归纳一下就是:记录小于等于0的数和最小的正数,对小于等于进行排序的数,对每个相邻的数,取差值的绝对值,如果有小于等于的正数的,就只输出小于等于0的个数,如果没有就加上1再输出。
#include <bits/stdc++.h>
using namespace std;
int a[100005];
#define MAX 1000000001
int main(void)
{
int t;
cin>>t;
while(t--)
{
int num1=0,minz=MAX,n;
cin>>n;
for(int i=1;i<=n;i++)
{
int m;
cin>>m;
if(m>0)
{
minz=min(minz,m);
}
if(m<=0)
{
a[++num1]=m;
}
}
if(minz==MAX)//这里一定要特判,否则后面会出错,而且可以加快速度
{
cout<<n<<endl;
continue;
}
sort(a+1,a+1+num1);
bool flag=true;
for(int i=2;i<=num1;i++)
{
if(abs(a[i]-a[i-1])<minz)
{
flag=false;
break;
}
}
if(flag)
{
num1++;
}
cout<<num1<<endl;
}
return 0;
}
C题就不更了,树形DP直接裂开
D. Kavi on Pairing Duty(区间DP)
题目大意:给你一个n,表示有2n个点,按要求对这些点进行两两配对,将两点连成线段,
要么线段之间长度相等,要么线段有包含关系
求这2n个点配对的方案数。
设一个数组dp[i],表示2i个点的方案数,
我们以长度为n来连接端点,就有2*(n-1)个端点在中间连不上,则方案数为dp[n-1]
我们以长度为n-1来连接端点,就有2*(n-2)个端点在中间连不上,则方案数为dp[n-2]
我们以长度为n-2来连接端点,就有2*(n-3)个端点在中间连不上,则方案数为dp[n-3]
依次类推,
直到以长度为n来连接端点,我们就会发现所有的点都能连上,方案数为dp[0]=1。
如果我们进一步地缩短线段长度,就会发现,有时候端点能全部连上,有时候不能,
能全部连上的时候,线段的长度为n的因数,那是因为有2n个端点,如果长度不是n的因数的话,必定会有余下的端点。
那么状态转移方程就出来了。
d
p
[
i
]
=
∑
j
=
0
i
−
1
d
p
[
j
]
+
s
i
z
[
i
]
−
1
dp[i]=\sum_{j=0}^{i-1} dp[j]+siz[i]-1
dp[i]=j=0∑i−1dp[j]+siz[i]−1
siz[i]为i的因数个数,由于dp[0]的时候加了一次(长度为i的时候),就要减去1。
#include <bits/stdc++.h>
using namespace std;
#define MAX 1000005
#define mod 998244353
int siz[MAX];
int dp[MAX];
int sum[MAX];
int main(void)
{
dp[0]=1;
sum[0]=1;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j+=i)
{
siz[j]++;
}
}
for(int i=1;i<=n;i++)
{
dp[i]=(sum[i-1]+siz[i]-1)%mod;
sum[i]=(dp[i]+sum[i-1])%mod;
}
cout<<dp[n];
return 0;
}