Educational Codeforces Round 98 Rated for Div. 2
比赛链接 https://codeforces.com/contest/1452
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197
B. Toy Blocks (二分)
链接:https://codeforces.com/contest/1452/problem/B
题意:有 n 个盒子,每个盒子有 a_i 个糖果。现在你可以向一些盒子中添加糖果,使得任选一个盒子,将其中的糖果分到其余 n-1 个盒子中,每个盒子的糖果数量相等。问最少需要添加多少糖果。 ( 1 ≤ n ≤ 1 0 5 , 0 ≤ a i ≤ 1 0 9 ) (1\le n \le 10^5,0\le a_i\le 10^9) (1≤n≤105,0≤ai≤109)
思路:一般来说答案就是最大的高度。然后补成 n − 1 n-1 n−1 的倍数就好了。也就不用二分了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
ll t,n;
ll a[maxn];
ll ans,sum;
bool check(ll mid)
{
ll tot=mid*(n-1);
return tot>=sum;
}
int main()
{
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
ll L=0,R=2e9;
sum=0;
for(int i=1; i<=n; ++i) scanf("%lld",&a[i]),L=max(L,a[i]),sum+=a[i];
while(L<R)
{
ll mid=(L+R)>>1;
if(check(mid)) R=mid;
else L=mid+1;
}
ans=L*(n-1)-sum;
printf("%lld\n",ans);
}
return 0;
}
D. Radio Towers (线性DP)
链接:https://codeforces.com/contest/1452/problem/D
题意:在区间 [ 0 , n + 1 ] [0,n+1] [0,n+1] 上设置灯塔。假设灯塔在 x i x_i xi ,照射半径为 r i r_i ri,那么就会照射 [ x i − r i , x i + r i ] [x_i-r_i,x_i+r_i] [xi−ri,xi+ri] 范围内的点。要求 0 0 0 和 n + 1 n+1 n+1 不被照射。 [ 1 , n ] [1,n] [1,n] 每个点仅被照射一次。其中 [ 1 , n ] [1,n] [1,n] 每个点设置灯塔的概率为 1 2 \frac 12 21 ,问符合条件的概率是多少。 ( 1 ≤ n ≤ 2 × 1 0 5 ) (1\le n \le 2\times 10^5) (1≤n≤2×105)
思路:设 dp[i] 表示 [1,i] 符合条件的方案数。
- d p [ i ] = d p [ i − 1 ] + d p [ i − 3 ] + d p [ i − 5 ] + … dp[i]=dp[i-1]+dp[i-3]+dp[i-5]+\dots dp[i]=dp[i−1]+dp[i−3]+dp[i−5]+…
- 因此由上式可得: d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] dp[i]=dp[i-1]+dp[i-2] dp[i]=dp[i−1]+dp[i−2]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5,N=2e5,mod=998244353;
int n,dp[maxn];
int qpow(int b,int n,int mod)
{
int res=1;
while(n)
{
if(n&1) res=1ll*res*b%mod;
b=1ll*b*b%mod;
n>>=1;
}
return res;
}
int main()
{
dp[0]=0,dp[1]=1;
for(int i=2; i<=N; ++i) dp[i]=(1ll*dp[i-1]+dp[i-2])%mod;
cin>>n;
int inv2=qpow(2,n,mod);
inv2=qpow(inv2,mod-2,mod);
cout<<1ll*dp[n]*inv2%mod<<"\n";
return 0;
}
E. Two Editorials (二次差分)
链接:https://codeforces.com/contest/1452/problem/E
题意:在区间 [1,n] 上有 m 个区间,你可以选择在 [1,n] 上选择两个长度为 k 的区间染色。上述的 m 个区间,只能取两个染色区间的其中一个计算染色的数量。问 m 个区间最多能够染色的长度是多少? ( 1 ≤ n , m ≤ 2000 , 1 ≤ k ≤ n , 1 ≤ l i ≤ r i ≤ n ) (1\le n,m\le 2000,1\le k \le n, 1\le l_i \le r_i\le n) (1≤n,m≤2000,1≤k≤n,1≤li≤ri≤n)
思路:
- 直接暴力的想,枚举两个染色区间的右端点,然后遍历 m 个区间,统计答案。时间复杂度 O ( n 2 m ) O(n^2m) O(n2m)
- 正确的做法是:先枚举第一个区间。统计出 m 个区间在第一个区间的贡献 a i a_i ai ,然后再统计第二个区间对 a i a_i ai 的贡献。可以这样想,第二个区间的右端点,从 k 开始一步一步往 n 移动。这其中,每次移动时,对一个特定的 a i a_i ai 产生的贡献也会发生变化。贡献首先会不断 + 1 ,然后是一段 0 ,最后是 -1 ,再是 0 。因此可以用两次差分来完成。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2010;
int n,m,k;
int l[maxn],r[maxn],a[maxn];
int cnt[maxn*10];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=m; ++i) scanf("%d%d",&l[i],&r[i]);
int ans=0;
for(int i=1; i+k-1<=n; ++i)
{
int res=0;
for(int j=1; j<=m; ++j)
{
a[j]=max(0,min(i+k-1,r[j])-max(i,l[j])+1);
res+=a[j];
}
memset(cnt,0,sizeof(cnt));
for(int j=1; j<=m; ++j)
{
int len=r[j]-l[j]+1;
if(k<=len)
{
cnt[l[j]+a[j]]++;
cnt[l[j]+k]--;
cnt[r[j]+1]--;
cnt[r[j]-a[j]+1+k]++;
}
else
{
cnt[l[j]+a[j]]++;
cnt[r[j]+1]--;
cnt[l[j]+k]--;
cnt[r[j]-a[j]+1+k]++;
}
}
int tmp=0;
for(int i=1; i<=n; ++i) cnt[i]+=cnt[i-1];
for(int i=1; i<=n; ++i) cnt[i]+=cnt[i-1],tmp=max(tmp,cnt[i]);
ans=max(ans,res+tmp);
}
printf("%d\n",ans);
return 0;
}