您的位置:首页 > 其它

Educational Codeforces Round 30 题解

2017-11-10 17:08 411 查看
比赛链接:http://codeforces.com/contest/873

A. Chores

题意:最大的k个数用x替换之后,求和。

#include <bits/stdc++.h>
using namespace std;

int main(){
int ans=0,n,k,x,a[110];
scanf("%d%d%d",&n,&k,&x);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=1; i<=n; i++){
if(n-i<k) ans+=x;
else ans+=a[i];
}
return 0*printf("%d\n", ans);
}

B. Balanced Substring
题意:给个01序列,求最长的连续区间使使得0的个数和1的个数相等。

解法:把0用-1替换,然后求扫一遍求前缀和,如果运到相等的更新答案,前缀和存在负数,所以用map来映射。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
char s[maxn];
int a[maxn];
unordered_map<int,int> pos;
int main(){
int n;
scanf("%d", &n);
scanf("%s", s);
for(int i=0; i<n; i++){
if(s[i]=='0') a[i+1]=-1;
else a[i+1]=1;
}
int sum = 0, ans = 0;
pos[0] = 1;
for(int i=1; i<=n; i++){
sum += a[i];
if(pos[sum]) ans = max(ans, i-pos[sum]+1);
else pos[sum] = i+1;
}
return 0*printf("%d\n", ans);
}

C. Strange Game On Matrix
题意:给你n*m的矩阵和k,让你在每一列的里找i最小的1,从这个1向下出发(包含),直到长度为len=min(k,n-i+1)。其中的1的个数加到总分数中。每一列找完之后,得到总分数。现在可以操作:把任意个1变为0。现在想要总分数最大。求最大总分数和此时操作的最小次数。

解法:列单独处理,贪心找最大的连续len里面1的个数。

#include <bits/stdc++.h>
using namespace std;
int n,m,K,a[110][110];
bool vis[110][110];
int mx,lim,pos,cnt,b[110];
int getmin(int x, int y){
return x<y?x:y;
}
int main(){
scanf("%d%d%d", &n,&m,&K);
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
scanf("%d", &a[i][j]);
}
}
int ans2 = 0;
memset(vis, 0, sizeof(vis));
for(int j=1; j<=m; j++){
mx = 0;
pos = 1;
for(int i=1; i<=n; i++) b[i] = a[i][j];
for(int i=1; i<=n; i++){
lim = K<(n-i+1)?K:(n-i+1);
cnt = 0;
for(int kk=i; kk<=i+lim-1; kk++){
if(b[kk]==1) ++cnt;
}
if(cnt>mx){
pos = i;
mx = max(mx, cnt);
}
}
for(int i=pos; i<=pos+(K<(n-pos+1)?K:(n-pos+1))-1; i++) vis[i][j]=1;
for(int i=1; i<pos; i++) if(a[i][j]==1) ans2++;
}
int ans1=0;
for(int i=1; i<=n; i++){
for(int j=1; j<=m; j++){
if(vis[i][j]){
ans1+=(a[i][j]==1);
}
}
}
return 0*printf("%d %d\n", ans1, ans2);
}

D. Merge Sort
题意:对于一个[l,r)的区间,如果是乱的,你会先mergesort一下,如果已经是递增的,那么就不用继续往下操作了;如果不是递增的,那么就要分一下,对左边[l,mid)操作,右边[mid,r)操作,等到左边和右边的都排完,然后最后再排一下,这样的操作次数是2。

解法:对于每一个样例,k肯定是个奇数,因为你最开始要操作一次全区间,然后才开始递归,而且k不会大于等于2*n,因为全部分开,操作最多也就是2*n-1(自己画一下图就知道了)。我们就从末状态开始,往前走,每次dfs把a[mid]和a[mid-1]换一下,因为a[mid-1]是在左边区间的,a[mid]是在右边区间的,所以你需要左边排一下,右边排一下,这样操作次数是2,最后排一下,就是这个最后排一下,可以把我们交换的还原。代码极短。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+6;
int n, k, a[maxn];
void cdq(int l, int r){
if(k==1||r<=l+1) return;
k-=2;
int mid = (l+r)/2;
swap(a[mid], a[mid-1]);
cdq(l,mid);
cdq(mid,r);
}
int main(){
scanf("%d%d",&n,&k);
if(k>=2*n||k%2==0) return 0*puts("-1");
for(int i=1; i<=n; i++) a[i]=i;
cdq(1,n+1);
for(int i=1; i<=n; i++) printf("%d ",a[i]); printf("\n");
}

E. Awards For Contestants
题意:给定n个正整数,要把它们放进三个组(分别为1号组,2号,3号),也可以不放。每组至少要有一个数。同时对于对于1,2,3号组,两两直接必须满足cnt[p]≤2*cnt[q],其中cnt[x]表示x号组有多少个数。对于两个数x,y,如果x < y,那么y不能放在编号比x大的组。 定义c[x]表示x号组的最大数,d[x]是x号组的最小数。特殊地,c[-1]表示没有放的数的最大值,d[-1]同理。现在求一个合法分配方案,最大化{d[1]-c[2],d[2]-c[3],d[3]-c[-1]}的字典序。n小于等于3000,正整数大小不超过5000

解法:先看正解。首先给n个数降序排序,容易发现这等价于把排序后的数组分成4段,其中前三段是要满足cnt两两不超过彼此两倍的条件。 然后可以枚举第1组的边界和第2组的边界,然后合法的第三组的边界就是一个区间,查询区间内的最大值即可,其中查最大值可以O(1)(利用RMQ)。然后CF上看到这个题可以直接暴力。。。O(n^3)虽然是达不到O(n^3)的。。

#include <bits/stdc++.h>
using namespace std;
#define c(x) a[x].val-a[x+1].val
struct node{
int id,val;
}a[3010];
bool cmp(const node &x,const node &y){
return x.val>y.val;
}
int n,x,y,z,tot1,tot2,ans1,ans2,ans3,p[4],Ans[3010];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
a[i].id=i;
scanf("%d",&a[i].val);
}
sort(a+1,a+n+1,cmp);
for(int i=1; i<=n; i++){
tot1 = min(n, i+i*2);
for(int j=i+(i+1)/2;j<=tot1;j++){
tot2=min(j+min(i,j-i)*2, n);
for(int k=j+max(i+1,j-i+1)/2;k<=tot2;k++){
if(c(i)>ans1||(c(i)==ans1&&c(j)>ans2)||(c(i)==ans1&&c(j)==ans2&&c(k)>ans3)){
ans1 = c(i), ans2 = c(j), ans3 = c(k), p[1] = i, p[2] = j, p[3] = k;
}
}
}
}
for(int i=1; i<=n; i++) Ans[a[i].id] = lower_bound(p+1,p+4,i)-p;
for(int i=1; i<=n; i++) printf("%d ", Ans[i]==4?-1:Ans[i]);
}

F. Forbidden Indices



#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200010;
const int M = 200010;
const int LOG = 18;
int mi
[LOG];
//后缀数组模板,0base
int n; //n表示原有字符串的长度
char s
; //s[i]表示原有字符串的第i位
int sa
; //sa[i]表示第i小的后缀对应的起点下标
int sb
; //sb[i]为辅助数组,表示起点下标为i,对应的后缀(倍增前一半)是第几小
int sc
; //sc[i]为辅助数组,表示第i小的后缀(倍增后一半)对应的起点下标
int cnt[M]; //cnt[i]为辅助数组,表示基数排序数值大小为i的字符的个数(以及前缀和)

//步骤一:建立后缀数组,求出sa[]
void build_sa(int charset) //charset是字符集的初始大小,同时也对应着当前基数排序数值的大小的上限
{
//利用指针的意义是减小数组交换的时间
int *x = sb, *y = sc;

//第一步:做区间边界差值为0的基数排序
for (int i = 0; i < charset; ++i)cnt[i] = 0; //初始化
for (int i = 0; i < n; ++i)++cnt[x[i] = s[i]]; //计数,{m==26的话,这里可以-'a'('A')}
for (int i = 1; i < charset; ++i)cnt[i] += cnt[i - 1]; //前缀和
for (int i = n - 1; i >= 0; --i)sa[--cnt[x[i]]] = i; //sa初始化

//第二步:倍增枚举区间边界差值len,len的最大也严格小于n
for (int len = 1; len < n; len <<= 1)
{
//用y[i]记录,以长度为len/2的后半段作关键字,把长度为len的字符串做排序后,第i小字符串的起点
int p = 0;
for (int i = n - len; i < n; ++i)y[p++] = i;
for (int i = 0; i < n; ++i)if (sa[i] >= len)y[p++] = sa[i] - len;

//再结合字符串前半段的关键字大小,
for (int i = 0; i < charset; ++i)cnt[i] = 0; //初始化
for (int i = 0; i < n; ++i)++cnt[x[i]]; //计数
for (int i = 1; i < charset; ++i)cnt[i] += cnt[i - 1]; //前缀和
for (int i = n - 1; i >= 0; --i)sa[--cnt[x[y[i]]]] = y[i]; //sa合并

//后缀大小排名重编号 (不要忘记,这是0base模板 最小后缀所对应下标是第0小)
p = 1; y[sa[0]] = 0;
for (int i = 1; i < n; ++i)
{
y[sa[i]] =
(x[sa[i - 1]] == x[sa[i]] && x[sa[i - 1] + len] == x[sa[i] + len]) ?
p - 1 : p++;
}
swap(x, y);
charset = p; //对字符集大小做上限更新
if (charset == n)break; //排序完毕
}
}
/*注意:这里遭遇了不以'$'封堵字符串尾端便使得答案有影响的问题,
我们发现问题其实出在后缀大小排名的重编号上。
我们依次枚举了当前排名为第i小的后缀,让它和前者比较双关键字的大小关系。
然后这里便可以发现,我们会遇到位置的溢出,我们用'$'封堵之后——
一旦某一段溢出了,它就会立刻显得与众不同,使得排序不出错。*/

//步骤二,求出rk[]数组以及height[]数组,定义height[i]=suffix(sa[i-1])和suffix(sa[i])的最长公共前缀
int rk
, height
;
int G;
void cal_height()
{
/*h[i]表示排名第rk[i]小的后缀与排名第rk[i]-1小后缀的最长公共前缀。
也就是以位置i为开头的后缀和比它排名小1后缀的最长公共前缀。
h数组有以下性质:h[i]>=h[i-1]-1。*/

for (int i = 0; i < n; ++i)
{
rk[sa[i]] = i;
}
for (int i = 0, k = 0; i < n; height[rk[i++]] = k)
{
if (k)--k; //用k来记录h[],每次-=1
if (rk[i]) for (int j = sa[rk[i] - 1]; s[i + k] == s[j + k]; ++k); //rk[]>1时才可以进行转移
}
/*
for (int i = 0; i < n; ++i)
{
printf("%d ", height[i]);
}puts("");
*/
}
void RMQinit(){
for(int i=0; i<n; i++) mi[i][0]=height[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=0; i+(1<<j)<=n; i++)
mi[i][j] = min(mi[i][j-1], mi[i+(1<<j-1)][j-1]);
}
int getLCP(int x, int y){
x++;
int k = log2(y-x+1);
return min(mi[x][k], mi[y-(1<<k)+1][k]);
}
char t
;
int main(){
scanf("%d", &n);
scanf("%s %s", s,t);
strrev(s);
strrev(t);
n++;
build_sa(128);
cal_height();
RMQinit();
LL ans = 0;
int st = 0;
vector <int> v1;
for(int i=0; i<n; i++){
if(t[sa[i]] == '1') continue;
if(st) v1.push_back(getLCP(st, i));
st = i;
if(i) ans = max(ans, 1LL*(n-sa[i]-1));
}
stack <int> len, pos;
for(int i=0; i<v1.size(); i++){
int now=i;
while(!len.empty()&&len.top()>=v1[i]){
ans = max(ans, (LL)len.top()*(i-pos.top()+1));
now = pos.top();
pos.pop();
len.pop();
}
pos.push(now);
len.push(v1[i]);
}
while(!len.empty()){
ans = max(ans, (LL)len.top()*(LL)(v1.size()-pos.top()+1));
len.pop();
pos.pop();
}
return 0*printf("%lld\n", ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: