【Codeforces】Educational Round 61

今天刚打完一场,心情异常烂,只做出来一道题,开错题了然后没钻出来,机房的小伙伴都切了四道题了...可能又要掉了,要继续努力了。

这次Edu Round比赛是这周第二次在宿舍请假了,等网安结束了就退宿吧。

比较顺利的做出了ABC,然后看人数去推了F题,不过没什么结果。

然后在改代码的时候结识了白学长,:)

话说这几天好燥啊,G题其实代码挺好理解但愣是拖了两天啊。

Problem A. Regular Bracket Sequence



题目大意:现在给出 4 种括号依次为 "((" , "()" , ")(" , "))" ,给出每种括号的数量,求所有的括号能不能构成一个合法的括号序列(所有括号都成对出现)

数据范围:1≤括号数量≤109

第二种括号显然对答案没有影响,第三种括号只要插在第一种和第四种中间就行,但第一种和第四种括号数量必须相等。

代码:只贴一行

if(cnt[1]==cnt[4]&&(cnt[1] || cnt[3]==0)){printf("1"); return 0;}

Problem B. Discounts



题目大意:现在你要购买 n 件物品,有 m 张优惠卷。使用一张优惠卷可以使某 qi 件物品中价格最低的物品免费,每次只能使用一张优惠卷。求每一张优惠卷使用后的最小开销。

数据范围:1≤m<n≤3*105,1≤物品价格≤109

排序按题意去掉第 q大的物品即可

代码:不贴了

Problem C. Painting the Fence



题目大意:在长度为 n 的区间内有 q 条线段,每条线段可以覆盖一段区间。现在要去掉两条线段,求剩下 q-2 条线段的最大覆盖长度

数据范围:3≤n,q≤5000

数据明显有坑...考虑到被移除的两条线段的关系:如果两条线段同时覆盖了一段区间,去掉这两条线段后公用的这部分可能不被覆盖。

在 3s 的时限下可以 O(n2) 枚举这两条线段,但是还有计算他们是否有重合部分及这部分对答案的影响。可以 O(n2) 暴力覆盖(或者优雅的差分,但本人差分学得不好)。

一条线段被移除后不被覆盖的区间是只被其覆盖1次的区间,两条线段移除后中间那部分(如果有)就是被覆盖2次的区间。所以 O(n) 预处理出1的前缀和与2的前缀和。

然后 O(n2) 枚举两条线段,统计最值。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 const int N=5050;
 7
 8 int n,q,ans=N;
 9 int color[N],tot,sum1[N],sum2[N];
10 struct sect{
11     int l,r;
12     int len;
13 }s[N];
14
15 int main(){
16     int a,b,cnt,l1,l2;
17     scanf("%d%d",&n,&q);
18     for(int i=1;i<=q;i++){
19         scanf("%d%d",&s[i].l,&s[i].r);
20         a=s[i].l; b=s[i].r;
21         for(int j=a;j<=b;j++)++color[j];
22     }
23     for(int i=1;i<=n;i++){
24         sum1[i]=sum1[i-1]+(color[i]==1);
25         sum2[i]=sum2[i-1]+(color[i]==2);
26         if(color[i])++tot;
27     }
28     for(int i=1;i<=q;i++)s[i].len=sum1[s[i].r]-sum1[s[i].l-1];
29     for(int i=1;i<=q;i++){
30         l1=s[i].len;
31         for(int j=1;j<=q;j++){
32             if(i==j)continue;
33             l2=s[j].len;
34             a=max(s[i].l,s[j].l);
35             b=min(s[i].r,s[j].r);
36             if(b>=a){
37                 cnt=sum2[b]-sum2[a-1];
38                 ans=min(ans,l1+l2+cnt);
39             }
40             else ans=min(ans,l1+l2);
41         }
42     }
43     ans=tot-ans;
44     printf("%d",ans);
45     return 0;
46 }

~

Problem D. Stressful Training



题目大意:一个教练带着他傻头傻脑的学生去参加一场计算机比赛,但是这帮人全部忘带了充电线!每一台电脑在比赛开始时有初始电量 ai ,每分钟耗费 bi 的电量。比赛时长 k 分钟。教练穷啊,所以他只卖了一条充电线。这条充电线的功率可以被选择,但是一旦确定就不能改变了。现在想知道选择功率最小是多少的充电线可以帮助学生们扛到比赛结束。

数据范围:1≤n,k≤2*10,1≤a≤1012,1≤b≤107

感觉可以二分,功率越大可以坚持的时间就越长,反之亦然。

那么怎么二分呢?看了别人的代码受到的启发: k 分钟就是可以用 k 次,然后在每台电脑没电的那个时候分配给他1次充电机会,然后给时间开一个桶,存某个时间分配的充电次数。对这个桶求前缀和,然后判断合不合法就行了。时间复杂度 O(n) 。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 typedef long long LL;
 7 const int N=200050;
 8
 9 int n,k;
10 LL a[N],b[N];
11 int s[N];
12 bool check(LL x){
13     for(int i=1;i<=k;i++)s[i]=0;
14     int res=k;
15     LL chg;
16     for(int i=1;i<=n;i++){
17         chg=a[i];
18         while(chg<k*b[i]){
19             if(!res)return 0;
20             ++s[chg/b[i]+1];
21             --res; chg+=x;
22         }
23     }
24     for(int i=1;i<=k;i++)s[i]+=s[i-1];
25     for(int i=1;i<=k;i++)if(s[i]>i)return 0;
26     return 1;
27 }
28 int main(){
29     scanf("%d%d",&n,&k); --k;
30     for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
31     for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
32     LL l=0,r=2000000000000;
33     LL mid,ans=-1;
34     while(l<=r){
35         mid=(l+r)>>1;
36         if(check(mid)){
37             ans=mid; r=mid-1;
38         }
39         else l=mid+1;
40     }
41     printf("%lld",ans);
42     return 0;
43 } 

~

Problem E. Knapsack



题目大意:有 8 种物品权值为 1~8 。求在 w 以内这些物品可以凑出来的最大价值。

数据范围:1≤w≤1018,1≤cnt≤1016

第一反应当然是背包啦。但是这么大的数据当然不行了。但是也存在别的动态规划的做法,不过没看懂。

下面提供的解法来着 FlyWhite 学长:

求所有物品的价值和,小于等于 w 就输出。否则

取 1~8 的最小公倍数 840 。如果可以取一些物品得到权值 x ,那么也可以得到 x+840*k 。

最后的结果就相当是从总价值和 sum 中去掉一些物品,他们的价值一定可以表示成 x+840*k

得到

sum-x-840*k1≤w

同样的 sum 也可以写成 x+840*k 的形式

相当于

(sum-x)%840+840*k≤w

现在只要确定对于每一个 x 的 k 的最大值就可以算出那个最大的 ans 了。

本来想着枚举 k 的,但是学长提供了更简洁的方法

令 mod=(sum-x)%840

mod+840*k+y=w

求 y 的最小值同样可以得到最大的 ans

y=w-840*k-mod

min(y)=(w-mod)%840

感觉很厉害有没有。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<bitset>
 5 using namespace std;
 6
 7 typedef long long LL;
 8 const LL M=840;
 9
10 LL w,a[10],sum,ans;
11 bitset<M> f;
12
13 int main(){
14     LL t;
15     scanf("%lld",&w);
16     for(int i=1;i<=8;i++){
17         scanf("%lld",&a[i]);
18         sum+=a[i]*i;
19     }
20     if(sum<=w){printf("%lld",sum); return 0;}
21     f[0]=1;
22     for(int i=1;i<=8;i++){
23         t=min(a[i],M/i);
24         for(int j=0;j<t;j++)f|=(f<<i);
25     }
26     for(int i=0;i<=840;i++) if(f[i]){
27         t=(sum-i)%M;
28         ans=max(ans,w-((w-t)%M+M)%M);
29     }
30     printf("%lld",ans);
31     return 0;
32 }

~

Problem F. Clear the String



题目大意:一个长度为 n 由小写拉丁字母组成的字符串 s ,如果它的一个字串的字母全部相等,就可通过一次操作以删除这个字串。求删除整个字符串最少需要多少次操作。

数据范围:1≤n≤500

感觉有点像祖玛。有一个 O(n2) 的 dp 方案。

令 f[i][j] 表示 i~j 段全部删除的最小操作数。

如果 i~j 段两端点字母相同,那么可以把 f[i][j-1] 和 f[i+1][j] 两段不花费代价合并,也可以先清除 f[i+1][j-1] 段。

否则就枚举断点,用 f[i][k]+f[k+1][j] 更新答案。

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 const int N=550;
 7
 8 int n;
 9 int f[N][N];
10 char s[N];
11
12 int main(){
13     scanf("%d",&n);
14     scanf("%s",s+1);
15     memset(f,0x3f,sizeof(f));
16     for(int i=1;i<=n;i++)f[i][i]=1;
17     for(int l=1;l<n;l++){
18         for(int i=1,j;i+l<=n;i++){
19             j=i+l;
20             if(s[i]==s[j]){
21                 f[i][j]=min(f[i][j-1],f[i+1][j]);
22                 if(l==1)f[i][j]=1;
23                 else f[i][j]=min(f[i+1][j-1]+1,f[i][j]);
24             }
25             else for(int k=i;k<j;k++){
26                 f[i][j]=min(f[i][k]+f[k+1][j],f[i][j]);
27             }
28         }
29     }
30     printf("%d",f[1][n]);
31     return 0;
32 }

~

Problem G. Greedy Subsequences



题目大意:求一个长度为 n 的序列的所有长度为 k 的子区间的最长上升子序列长度。

数据范围:1≤n,k≤106

感觉收获蛮多的一道题了,虽然拖得略久了点。

回忆一些我们是怎么求最长上升子序列的:

令 f[i] 表示以i结尾的最长上升子序列的长度,每次从i之前的所有位置 a[x]<a[i] 的 f[x] 转移过来。

现在我们令 s[i] 表示以第i位开头的最长上升子序列长度。当i之后出现一个大于i的数字时,++s[i]。

在当前的前i-1位的s[i]计算好时,把第i个数字放在第i个位置,这是需要把前i-1个位置上所有小于a[x]<a[i]的s[x]更新。

如果我们求出了每个数字前面比它小的数字是那些,只要在这个数字放进这个序列的时候更新这些位置上的s就可以了。

我们用一个栈倒着把当前的数字和栈顶比较,若a[i]>a[stack[top]]就把stack[top]左边第一个比它大的数字的位置 i 存下来(pre[i])。然后把a[i]入栈。

之后我们就得到了每个数字左边第一个比它的的数字的位置。

我们开始挨个把数字接到现在的序列的后面,然后把他自己的位置 i 到 pre[i]+1 这一段的s区间加1。

再查询 i-k+1~i这个区间的最大值,就是这个区间的最长上升子序列的长度了~

代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5
 6 const int N=1000050;
 7
 8 int n,k;
 9 int a[N], sta[N],top,pre[N];
10 int s[N<<2],tag[N<<2];
11
12 void update(int rt,int c){
13     s[rt]+=c;
14     tag[rt]+=c;
15 }
16 void PushUp(int rt){
17     s[rt]=max(s[rt<<1],s[rt<<1|1]);
18 }
19 void PushDown(int rt){
20     if(tag[rt]){
21         update(rt<<1,tag[rt]); update(rt<<1|1,tag[rt]);
22         tag[rt]=0;
23     }
24 }
25 void add(int L,int R,int l,int r,int rt){
26     if(L<=l&&r<=R)return update(rt,1);
27     PushDown(rt);
28     int mid=(l+r)>>1;
29     if(L<=mid)add(L,R,l,mid,rt<<1);
30     if(R>mid)add(L,R,mid+1,r,rt<<1|1);
31     PushUp(rt);
32 }
33 int query(int L,int R,int l,int r,int rt){
34     if(L<=l&&r<=R)return s[rt];
35     PushDown(rt);
36     int mid=(l+r)>>1,ret=0;
37     if(L<=mid)ret=max(ret,query(L,R,l,mid,rt<<1));
38     if(R>mid)ret=max(ret,query(L,R,mid+1,r,rt<<1|1));
39     return ret;
40 }
41
42 int main(){
43     scanf("%d%d",&n,&k);
44     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
45     for(int i=n;i;i--){
46         while(top&&a[sta[top]]<=a[i])pre[sta[top]]=i,--top;
47         sta[++top]=i;
48     }
49     for(int i=1;i<=n;i++){
50         add(pre[i]+1,i,1,n,1);
51         if(i>=k)printf("%d ",query(i-k+1,i,1,n,1));
52     }
53     return 0;
54 }

~

原文地址:https://www.cnblogs.com/opethrax/p/10498666.html

时间: 2024-07-29 15:17:05

【Codeforces】Educational Round 61的相关文章

【题解】CH Round #61 取数游戏

取数游戏 CH Round #61 - 「Adera 10」冬令营热身赛 描述 SJY和CYF在玩一个取数游戏.他们将1~n分别写在n张纸上,随机排成一排,约定SJY先取,只能取走最边上的两张纸之一,然后CYF取:以此循环下去,取到1的人获胜.假设SJY和CYF足够聪明,求SJY获胜的概率. 输入 一个整数n 输出 SJY获胜的概率,保留最简分数形式(若为1,则输出1/1:若为0,则输出0/1). 样例 样例输入1 2 样例输出1 1/1 样例输入2 3 样例输出2 2/3 数据范围与约定 40

【Codeforces Round 1132】Educational Round 61

Codeforces Round 1132 这场比赛做了\(A\).\(B\).\(C\).\(F\)四题,排名\(89\). \(A\)题\(wa\)了一次,少考虑了一种情况 \(D\)题最后做出来,但被\(hack\)了...被\(hack\)的原因是没有想到答案会超过\(10^{12}\)(毕竟这个时间上的优化也是在最后迫不得已的情况下加的,就没有考虑正确性... Codeforces 1132 C 题意:给一些区间\([l_i,r_i]\),从中删掉两个,求剩下的区间最多能够覆盖的格子数

【codeforces】Educational Codeforces Round 80 D. Minimax Problem——二分+二进制处理

题目链接 题目大意 有n个维度为m的向量,取其中两个进行合并,合并时每个维度取两者之间的较大者,得到的新的向量中,维度值最小者最大为多少 分析 首先最需要注意的是m的取值,m最大只有8 那么我们可以二分答案,对于每一个二分值,进行下面的操作 将整个矩阵的每一个元素,如果这个元素大于二分值,则变成1,反正则变成0 把每一个向量压缩为单个二进制数 这样我们最多只会得到\(2^8 = 256\)种不同的二进制数,然后暴力的遍历所有可能的二进制数的组合,得到是否满足当前二分值 AC code #incl

【codeforces】【比赛题解】#915 Educational CF Round 36

虽然最近打了很多场CF,也涨了很多分,但是好久没写CF的题解了. 前几次刚刚紫名的CF,太伤感情了,一下子就掉下来了,不懂你们Div.1. 珂学的那场我只做了第一题--悲伤. 这次的Educational Round打的还可以,虽然吧没有涨分(因为我是紫色的啊). 做了前4题,后面3题也比较简单,陆续也做完了. 所以心情好,来写一篇题解! [A]花园 题意: 长度为\(k\)的线段,用若干个长度为\(a_i\)的线段,正好覆盖.(\(a_i|k\)) 给定\(n\)个\(a_i\),求出最小的\

【codeforces】【比赛题解】#854 CF Round #433 (Div.2)

cf一如既往挺丧 看丧题点我! [A]分数 Petya是数学迷,特别是有关于分数的数学.最近他学了所谓一个分数被叫做"真分数"当且仅当其分子小于分母,而一个分数被叫做"最简分数"当且仅当其分子分母互质.在闲暇时间,Petya在用计算器研究:如何把最简真分数转换为小数等问题.有一天他不小心把除号(÷)按成了加号(+),导致他得到了分子与分母的和.Petya想要得到他原来的分数,但他很快发现这不是唯一的.所以现在他想要知道最大的最简真分数使得其分子与分母的和为n. 输入

【codeforces】【比赛题解】#849 CF Round #431 (Div.2)

cf的比赛越来越有难度了--至少我做起来是这样. 先看看题目吧:点我. 这次比赛是北京时间21:35开始的,算是比较良心. [A]奇数与结束 "奇数从哪里开始,又在哪里结束?梦想从何处起航,它们又是否会破灭呢?" 给定一个长度为n的序列.确定能不能将序列分成奇数个长度为奇数的非空字串,而且这其中每个子串以奇数开头,以奇数结尾.可以只分成一个(1也是奇数). 输入 第一行一个正整数n,表示序列长度. 第二行n个整数,表示序列中的元素. 输出 输出"Yes"或"

【CodeForces】841C. Leha and Function(Codeforces Round #429 (Div. 2))

[题意]定义函数F(n,k)为1~n的集合中选择k个数字,其中最小数字的期望. 给定两个数字集A,B,A中任意数字>=B中任意数字,要求重组A使得对于i=1~n,sigma(F(Ai,Bi))最大. [算法]数学结论+数学期望+排序 [题解]很无奈,这题放在div2 C,难以推导的期望公式,广为人知的结论,容易观察样例得出的做法,都体现了这道题的不合理性. F(n,k)=(n+1)/(k+1) 公式推导可能触及我的知识盲区了QAQ 得到公式后,显然要求k尽可能小,n尽可能大,经验告诉我们随着两数

【CodeForces】704 C. Black Widow 动态规划+模拟

[题目]C. Black Widow [题意]给定一个表达式,形式为(...)^(...)^......^(...)=1(n个括号),括号中为1~2个值取或.有m个变量,给出表达式的值为xi或 !xi,xi只能为0或1,求变量赋值使得表达式成立的方案数.每个变量至多出现两次.n,m<=10^5. [算法]动态规划+模拟 [题解]每个括号视为一个点,对于同时出现在两个括号内的变量将两个点连边(边须记两边异同).由于每个变量至多出现两次,所以一条边就可以代表一个变量. 由于每个括号至多两个变量,所以

【codeforces】940F题解

CF Round #466的最后一题,颇有难度,正解是带修改莫队算法. [题意] 给定一个长度为\(n\)的数组\(a\),并且要求执行\(q\)个操作,有两种不同的操作: ①询问一个区间\([l,r]\)中集合\(\left\{c_{0},c_{1},c_{2},\cdots,c_{10^9}\right\}\)的Mex,而\(c_i\)表示数值\(i\)在\([l,r]\)中的出现次数. ②把\(a_p\)修改成\(x\). 对每一个询问输出答案. [题解] 典型的区间问题,不要求在线,可以