Educational Codeforces Round 66 Rated for Div. 2 Jun/05/2019 22:35UTC+8
比赛链接 https://codeforces.com/contest/1175
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197
B. Catch Overflow! (模拟栈操作)
链接:https://codeforces.com/contest/1175/problem/B
题意:给出 q 个指令,3 种类型:
- for n :循环 n 次
- end :结束一层循环
- add :对 x 加 1
x 初始为 0,输出 x 最终的值。如果 x 大于 2 32 − 1 2^{32}−1 232−1 ,则输出 “OVERFLOW!!!”
思路:用栈来模拟循环的指令
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
const ll lim=(1ll<<32)-1;
int q,x;
ll sta[maxn],top=0,ans;
string op;
int main()
{
bool ok=1;
cin>>q;
sta[0]=1;
while(q--)
{
cin>>op;
if(op=="add")
{
ans+=sta[top];
if(ans>lim) ok=0;
}
else if(op=="for")
{
cin>>x;
sta[++top]=min(sta[top-1]*x,lim+1);
}
else top--;
}
if(!ok) puts("OVERFLOW!!!");
else cout<<ans<<"\n";
return 0;
}
C. Electrification (绝对值 + 思维)*
链接:https://codeforces.com/contest/1175/problem/C
题意:给定 n 、 k n、k n、k 长度为 n 的数组 a a a 。 a i a_i ai 表示 X X X 轴上的点。请你找一个整数 x x x ,使得第 k + 1 k+1 k+1 小的 ∣ a i − x ∣ |a_i-x| ∣ai−x∣ 最小,可能有多个解,输出任何一个解
思路: ∣ a i − x ∣ |a_i-x| ∣ai−x∣ 就是 a i a_i ai 跟 x x x 的距离。要想使得第 k+1 个值最小,只需要取 k+1 个最近的点,然后取它们的中点就好了。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int t,n,k;
int a[maxn];
int main()
{
cin>>t;
while(t--)
{
cin>>n>>k;
for(int i=1;i<=n;++i) cin>>a[i];
ll dis=9e18,ans;
for(int i=1;i+k<=n;++i)
{
if(dis>a[i+k]-a[i])
{
dis=a[i+k]-a[i];
ans=(a[i+k]+a[i])/2;
}
}
cout<<ans<<"\n";
}
return 0;
}
D. Array Splitting (前缀和)
链接:https://codeforces.com/contest/1175/problem/D
题意: 给定长度为 n n n 个数组,划分成 k k k 个非空子集,要求 ∑ i = 1 n a i × f ( i ) \sum_{i=1}^n a_i\times f(i) ∑i=1nai×f(i)最大。 f ( i ) f(i) f(i) 表示第 i i i 个元素在第 f ( i ) f(i) f(i) 个子集。 ( 1 ≤ k ≤ n ≤ 3 × 1 0 5 , 1 ≤ ∣ a i ∣ ≤ 1 0 6 ) (1\le k \le n \le 3\times 10^5,1\le |a_i| \le 10^6) (1≤k≤n≤3×105,1≤∣ai∣≤106)
思路:画个简单的示意图就会发现,其实,所有 元素的和 + k − 1 k - 1 k−1 个最大后缀和。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+5;
int n,k;
ll a[maxn],ans;
int main()
{
cin>>n>>k;
for(int i=1; i<=n; ++i) cin>>a[i];
for(int i=n; i>=1; --i) a[i]=a[i]+a[i+1];
ll ans=a[1];
sort(a+2,a+1+n);
for(int i=n; i>=n-k+2; --i) ans+=a[i];
cout<<ans<<"\n";
return 0;
}
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
const ll lim=(1ll<<32)-1;
int n,k;
int a[maxn];
ll pref[maxn];
int main()
{
ll sum=0;
cin>>n>>k;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i)
{
pref[i]=pref[i-1]+a[i];
sum+=a[i];
}
sort(pref+1,pref+n);
ll ans=sum*k;
for(int i=1;i<=k-1;++i)
{
ans-=pref[i];
}
cout<<ans<<"\n";
return 0;
}
E. Minimal Segment Cover(倍增)
题意:给定 n 条线段,给出 m 个区间的询问,问最少需要多少条线段可以覆盖当前询问的区间。
(
n
,
m
≤
2
e
5
,
l
i
,
r
i
≤
5
e
5
)
(n,m\le 2e5 , l_i,r_i\le 5e5)
(n,m≤2e5,li,ri≤5e5)
思路:
- 设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示当前位置 i 通过 2 j 2^j 2j 条线段能够到达的最远距离
- 预处理出,当前位置 i 通过 1条线段能够到达的最远距离,通过2条线段能够到达的最远距离,通过4条线段能够到达的最远距离。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e5;
int n,m,maxr;
int dp[maxn+10][25];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
int l,r;
scanf("%d%d",&l,&r);
dp[l][0]=max(dp[l][0],r);
maxr=max(maxr,r);
}
for(int l=1;l<=maxr;++l)
dp[l][0]=max(dp[l][0],max(l,dp[l-1][0]));
for(int j=1;j<=20;++j)
for(int i=0;i<=maxr;++i)
dp[i][j]=dp[dp[i][j-1]][j-1];
while(m--)
{
int l,r;
ll ans=0;
scanf("%d%d",&l,&r);
if(r>maxr||dp[l][20]<r)
{
puts("-1");
continue;
}
for(int i=20;i>=0;--i)
if(dp[l][i]<r)
l=dp[l][i],ans+=(1<<i);
ans++;
printf("%lld\n",ans);
}
return 0;
}
F. The Number of Subpermutations(思维 + rmq || 分治)
题意:给以一个长度为 n 的数组,求区间
[
l
,
r
]
[l,r]
[l,r] 是排列的个数
思路:
- 排列需要满足,区间长度等于最大值,且每个数只出现一次。
- 因此处理出每个点 i 向右最多的不同的数字个数。然后就可以枚举每个点,在 i 和 r[i]中找可能的答案
实现:
- 从 n 到 1,记录每个点 i 上次出现的位置,与点 i + 1 取个min
- 用 rmq 处理一下最大值的查询
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+10;
int n;
int a[maxn],last[maxn];
int dp[maxn][20],r[maxn];
void init()
{
for(int i=1;i<=n;++i) dp[i][0]=a[i];
for(int j=1;(1<<j)<=n;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}
int queryMax(int l,int r)
{
int k=log2(r-l+1);
return max(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
r[n+1]=n;
for(int i=n;i>=1;--i)
{
if(last[a[i]]) r[i]=min(r[i+1],last[a[i]]-1);
else r[i]=r[i+1];
last[a[i]]=i;
}
init();
int ans=0;
for(int i=1;i<=n;++i)
{
int j=i;
while(j<=r[i])
{
if(queryMax(i,j)==j-i+1) ans++,j++;
else j=i+queryMax(i,j)-1;
}
}
printf("%d\n",ans);
return 0;
}
学长的分治代码
#include <bits/stdc++.h>
using namespace std;
#define MAXNUM 333333
#define rep(i,s,t) for(int i=s;i<t;i++)
#define pii pair<int,int>
int per[MAXNUM],pos[MAXNUM],f[20][MAXNUM],LOG[MAXNUM],n;
pii fnum[20][MAXNUM],num[MAXNUM];
void getlist()
{
LOG[0] = -1;
for (int i = 1; i <= n; i++)
LOG[i] = LOG[i / 2] + 1;
}
template<typename T> void create(T f[][MAXNUM],T num[])
{
for (int j = 1; j <= n; j++)
f[0][j] = num[j];
for (int i = 1; i <= 20; i++)
for (int j = 1; j + (1 << i) - 1 <= n; j++)
f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
}
template<typename T> T query(T f[][MAXNUM],int qleft, int qright)
{
int nLog = LOG[qright - qleft + 1];
return max(f[nLog][qleft], f[nLog][qright - (1 << nLog) + 1]);
}
int res;
void solve(int l,int r)
{
if(l>r)return;
pii p=query(fnum,l,r);
int mid=(l+r)/2;
if(p.second<=mid)
{
rep(i,l,p.second+1)
if(i+p.first-1<=r&&i+p.first-1>=p.second&&query(f,i,i+p.first-1)<i)
res++;
}
else{
rep(i,p.second,r+1)
if(i-p.first+1>=l&&i-p.first+1<=p.second&&query(f,i-p.first+1,i)<i-p.first+1)
res++;
}
solve(l,p.second-1),solve(p.second+1,r);
}
int main()
{
scanf("%d",&n);
getlist();
rep(i,1,n+1)scanf("%d",&num[i].first),num[i].second=i;
rep(i,1,n+1)
per[i]=pos[num[i].first],pos[num[i].first]=i;
create(f,per),create(fnum,num);
solve(1,n);printf("%d\n",res);
}