A:Marketing Scheme
满足
r
≥
2
∗
l
r\ge2*l
r≥2∗l无解。
B:Reverse Binary Strings
这题应该注重结果而不是交换的过程。我们可以发现,对于一个串来说,一定发生了下面这种情况:
C:Chef Monocarp
先考虑贪心,肯定尽量会往它本身的那个时刻放,剩下的往本身时刻的两边放所造成的贡献更小。但是这样会出现一个矛盾,比如时刻7有5个,时刻9有5个,这样就会造成中间位置的竞争。
然后我们考虑到这样的竞争结果是不好预测的,无论是让给左边还是让给右边都会取决于后面其他位置的影响。前面的选择的结果会影响后面,这便可以考虑DP的解题策略了。
我们用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示在时刻
j
j
j放置前
i
i
i个物品所需要的消耗,很容易得出转移方程是
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
−
1
]
,
f
[
i
−
1
]
[
j
−
1
]
+
a
b
s
(
j
−
a
[
i
]
)
)
f[i][j]=max(f[i][j-1],f[i-1][j-1]+abs(j-a[i]))
f[i][j]=max(f[i][j−1],f[i−1][j−1]+abs(j−a[i])).即
j
j
j时刻放物品
i
i
i,或者
j
j
j时刻不放物品
i
i
i.注意,在这个表达式下,物品
i
i
i一定放在物品
i
+
1
i+1
i+1前面,因此,我们一定要通过排序,保证
t
i
<
t
i
+
1
t_i<t_{i+1}
ti<ti+1!
最后就是DP初始化的问题:一开始的合法状态必然是
f
[
0...
n
]
[
0
]
=
0
f[0...n][0]=0
f[0...n][0]=0.
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
int n,m,e,ans=0;
int a[210],f[410][410];
const int INF=0x3f3f3f3f;
int main()
{
close;
int T;cin>>T;
while(T--)
{
int n;cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
memset(f,INF,sizeof(f));
for(int j=0;j<=400;++j) f[0][j]=0;
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
for(int j=1;j<=400;++j)
f[i][j]=min(f[i][j-1],f[i-1][j-1]+abs(j-a[i]));
int maxnum=INT_MAX;
for(int j=n;j<=400;++j) maxnum=min(maxnum,f[n][j]);
cout<<maxnum<<endl;
}
}
D:Minimal Height Tree
题目的限制条件是一个根结点的儿子的结点编号是呈递增的,因此我们只要贪心地将数组
a
a
a当中尽可能长的递增序列安排到一个结点下方做他的子结点即可。
E:Make It Increasing
【前置准备:HDU5256 序列变换】
题目大意是,给定一个数列A,让你修改数量最少的元素,使得这个数列严格递增。我们可以这么考虑,如果有
∀
i
,
A
[
i
+
1
]
>
A
[
i
]
\forall{i},A[i+1]>A[i]
∀i,A[i+1]>A[i],那么则有
A
[
i
+
1
]
−
1
≥
A
[
i
]
A[i+1]-1\ge A[i]
A[i+1]−1≥A[i],进而有
A
[
i
+
1
]
−
(
i
+
1
)
≥
A
[
i
]
−
i
A[i+1]-(i+1)\ge A[i]-i
A[i+1]−(i+1)≥A[i]−i.我们只要得到修改后序列的最长非下降子序列的长度
l
e
n
len
len,那么只要修改
n
−
l
e
n
n-len
n−len个数,原序列就是严格上升的。
注意这里替换的时候要使用upper_bound()函数,替换掉第一个比他大的数字!使用lower_bound()函数是错误的,例如序列A=[1,3,4,6,4,4,5],使用lower_bound()得到的最终结果是1,3,4,5;而使用upper_bound()得到的最终结果是1,3,4,4,4,5.
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=5e5+100;
int a[maxn],b[maxn];
int solve(int n)
{
int len=1;
b[0]=a[0];
for(int i=1;i<n;++i)
{
if(a[i]>=b[len-1]) b[len++]=a[i];
else{
int pos=upper_bound(b,b+len,a[i])-b;
b[pos]=a[i];
}
}
return len;
}
int main()
{
close;
int T;cin>>T;
for(int casenum=1;casenum<=T;++casenum)
{
int n;cin>>n;
for(int i=0;i<n;++i) cin>>a[i],a[i]-=i;
printf("Case #%d:\n%d\n",casenum,n-solve(n));
}
}
CF上这道题唯一的区别就是多了几个固定位的限制。可以把问题分情况讨论一下:
①当
k
=
0
k=0
k=0时,问题退化成上面的形式,直接变换后求最长非下降子序列;
②当
k
>
0
k>0
k>0时,先检查是否满足
(
a
[
b
[
i
+
1
]
]
−
a
[
b
[
i
]
]
−
1
)
≥
(
b
[
i
+
1
]
−
b
[
i
]
−
1
)
(a[b[i+1]]-a[b[i]]-1)\ge(b[i+1]-b[i]-1)
(a[b[i+1]]−a[b[i]]−1)≥(b[i+1]−b[i]−1),不满足直接输出-1;
③否则,我们将上述序列按照b划分成一个个区间,分别在区间上求最长非下降子序列。可以设置数组a和数组b的哨兵结点,然后在区间
[
l
,
r
]
[l,r]
[l,r]上求解最长非下降子序列即可。注意:每个区间的第一个数值不能被修改!得到一个最长非下降子序列后,我们找到右端点在序列中第一个比他大的元素的位置pos,可以得知在最长非下降子序列
f
[
1...
p
o
s
−
1
]
f[1...pos-1]
f[1...pos−1]的元素可以不用被修改,
f
[
p
o
s
.
.
.
l
e
n
]
f[pos...len]
f[pos...len]需要被修改,所以该区间需要修改
(
r
−
l
+
1
)
−
p
o
s
(r-l+1)-pos
(r−l+1)−pos个。
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=5e5+100;
int a[maxn],b[maxn],f[maxn];
bool check(int k)
{
for(int i=2;i<=k;++i)
if((a[b[i]]-a[b[i-1]])<=(b[i]-b[i-1]-1)) return false;
return true;
}
int solve(int l,int r)
{
int len=1;
f[1]=a[l];
for(int i=l+1;i<=r;++i)
{
if(a[i]>=f[len]) f[++len]=a[i];
else{
int pos=upper_bound(f+1,f+len+1,a[i])-f;
if(pos!=1) f[pos]=a[i];
}
}
int pos=upper_bound(f+1,f+len+1,a[r])-(f+1);
return (r-l+1)-pos;
}
int main()
{
close;
int n,k;cin>>n>>k;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=k;++i) cin>>b[i];
if(k==0)
{
for(int i=1;i<=n;++i) a[i]-=i;
int len=1;
f[1]=a[1];
for(int i=2;i<=n;++i)
{
if(a[i]>=f[len]) f[++len]=a[i];
else{
int pos=upper_bound(f+1,f+len+1,a[i])-f;
f[pos]=a[i];
}
}
cout<<n-len<<endl;
return 0;
}
if(!check(k)) {cout<<-1<<endl;return 0;}
for(int i=1;i<=n;++i) a[i]-=i;
a[0]=INT_MIN;a[n+1]=INT_MAX;//设立哨兵结点
b[0]=0;b[k+1]=n+1;
int ans=0;
for(int i=1;i<=k+1;++i)
ans+=solve(b[i-1],b[i]);
cout<<ans<<endl;
}
F:Emotional Fishermen
题目大意:给定一个有
n
n
n个正整数的序列
a
a
a,目标是能够使排列后的数组满足:
a
p
i
m
a
x
j
=
1
i
−
1
a
p
j
∉
(
1
2
,
2
)
\frac{a_{p_i}}{max_{j=1}^{i-1}a_{p_j}} \notin (\frac {1}{2},2 )
maxj=1i−1apjapi∈/(21,2)问有多少种排列方案?
先将序列
a
a
a按照从小到大的顺序进行排列,我们用
d
p
[
i
]
dp[i]
dp[i]表示最大值为
a
i
a_i
ai时的方案个数,用
p
o
s
[
i
]
pos[i]
pos[i]表示从
[
1...
i
]
[1...i]
[1...i]中满足
2
∗
a
j
≤
a
i
2*a_j\le a_i
2∗aj≤ai中最大的
j
j
j.
先考虑如何求解
p
o
s
[
i
]
pos[i]
pos[i]的问题。这里我们可以采用单调栈的方法(通过模拟实现)。我们令
c
u
r
cur
cur指针指向的是对于当前的
a
i
a_i
ai来说满足条件的
a
j
a_j
aj,当
a
i
a_i
ai增大的时候,
c
u
r
cur
cur同样右移。
接着我们考虑如何构造的问题。现在我们拿到了一个最大值为
a
j
a_j
aj的序列,然后我们试图朝这个序列添加一个最大值为
a
i
a_i
ai,且满足
a
i
≥
a
j
a_i\ge a_j
ai≥aj.第一步,我们先将
a
i
a_i
ai放在当前序列从左向右看第一个空位上!这是我们构造的基础,也是我们后面正确性证明的必要条件。这时候我们就发现,这时候有
p
o
s
[
i
]
−
p
o
s
[
j
]
−
1
pos[i]-pos[j]-1
pos[i]−pos[j]−1个数,可以放在
n
−
p
o
s
[
j
]
−
2
n-pos[j]-2
n−pos[j]−2个空位上。(原来的序列有
p
o
s
[
j
]
+
1
pos[j]+1
pos[j]+1个,
a
i
a_i
ai在第一个空位上,这一位也不可选;这时候会有
p
o
s
[
i
]
−
p
o
s
[
j
]
−
1
pos[i]-pos[j]-1
pos[i]−pos[j]−1可以随意放在
a
i
a_i
ai后的任意空位上,一定满足条件。)因此,我们可以得出,递推方程是:
d
p
[
i
]
=
∑
j
=
0
i
−
1
d
p
[
j
]
∗
A
n
−
p
o
s
[
j
]
−
2
p
o
s
[
i
]
−
p
o
s
[
j
]
−
1
dp[i]=\sum_{j=0}^{i-1} dp[j]*A_{n-pos[j]-2}^{pos[i]-pos[j]-1}
dp[i]=j=0∑i−1dp[j]∗An−pos[j]−2pos[i]−pos[j]−1很明显我们最终的答案是
d
p
[
n
]
dp[n]
dp[n],当且仅当
p
o
s
[
n
]
=
n
−
1
pos[n]=n-1
pos[n]=n−1.
现在考虑一下构造的合理性,这样的构造方式会不会造成重复计数的问题。例如我们令
a
k
<
a
j
<
a
i
a_k<a_j<a_i
ak<aj<ai,计算
d
p
[
j
]
dp[j]
dp[j]使用了
d
p
[
k
]
dp[k]
dp[k],计算
d
p
[
i
]
dp[i]
dp[i]同时使用了
d
p
[
j
]
,
d
p
[
k
]
dp[j],dp[k]
dp[j],dp[k].但是根据我们的构造方式,能够保证不同的转移,最大值的出现次序一定是不同的,因为我们的最大值一定放在当前序列从左到右的第一个空位上,因此肯定不同。
最后一定要令
p
o
s
[
i
]
=
−
1
pos[i]=-1
pos[i]=−1,这样能够使得
n
−
p
o
s
[
0
]
−
2
=
n
−
1
,
p
o
s
[
i
]
−
p
o
s
[
j
]
−
1
=
p
o
s
[
i
]
n-pos[0]-2=n-1,pos[i]-pos[j]-1=pos[i]
n−pos[0]−2=n−1,pos[i]−pos[j]−1=pos[i],满足实际情况。
#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int mod=998244353;
const int maxn=5e3+100;
typedef long long ll;
ll qpow(ll base,ll x)
{
ll ans=1;
while(x) {if(x&1) ans=ans*base%mod;base=base*base%mod;x>>=1;}
return ans;
}
ll f[maxn],inv[maxn],a[maxn],pos[maxn],dp[maxn];
void Init(int n)
{
f[0]=1;inv[0]=1;
for(int i=1;i<=n;++i) f[i]=f[i-1]*i%mod;
inv[n]=qpow(f[n],mod-2);
for(int i=n-1;i>0;--i) inv[i]=inv[i+1]*(i+1)%mod;
}
ll A(ll n,ll m) {return f[n]*inv[n-m]%mod;}
int main()
{
close;int n;cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
sort(a+1,a+n+1);
Init(n);
int cur=0;
for(int i=1;i<=n;++i)
{
while(cur<n && a[i]>=2*a[cur+1]) cur++;
pos[i]=cur;
}
if(pos[n]!=n-1) {cout<<0<<endl;return 0;}
dp[0]=1;pos[0]=-1;
for(int i=1;i<=n;++i)
for(int j=0;j<=pos[i];++j)
dp[i]=(dp[i]+dp[j]*A(n-2-pos[j],pos[i]-pos[j]-1))%mod;
cout<<dp[n]<<endl;
}