Codeforces Round #510

今天看机房大佬们找老师要外网打CF比赛,我忍不住也跟着注册了个号参加了,这是我第一次打CF比赛,打得比较菜,可能还有讨论成分,请见谅哈。

传送门

T1:Benches

题意:有$n$个长凳,初始每个长凳上面坐着$a_i$个人,现在又来了m个人,每个人会选一个长凳坐下。设新来的m个人都坐下后,人数最多的一个长凳的人数为$k$,求$k$的最小值和最大值。

题解:$k$最小:把新来的人尽量往人少的长凳上放,如果放到所有长凳人数都相等的话就均分剩下的人;$k$最大:把m个人都放到初始人数最多的长凳上。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0; bool f=1; char c=getchar();
 5     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 6     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 7     if(f) return x;
 8     return 0-x;
 9 }
10 int n,m,a[10005],mx,cha_sum;
11 int main(){
12     n=read(),m=read();
13     for(int i=1;i<=n;i++) mx=max(mx,a[i]=read());
14     for(int i=1;i<=n;i++) cha_sum+=mx-a[i];
15     printf("%d %d\n", m-cha_sum>0 ? mx+(m-cha_sum+(n-1))/n : mx, mx+m);
16     return 0;
17 }
View Code

T2:Vitamins

题意:有$n$瓶果汁,第i瓶的价格为$c_i$,果汁中可能包含A, B, C三种维生素。问喝齐A, B, C三种维生素所需的买果汁的最小价钱。

题解:

状态压缩3种维生素的8种存在情况,dp即可。

转移就是从其它各种能够转移的情况转移过来。我写的比较粗暴,直接设了三维状态表示每种维生素是否存在。

“从其它各种能够转移的情况转移过来”主要是因为题目只要求喝齐三种维生素,没要求每种只能喝一个,因此一种维生素可以喝多个,即存在某种维生素的状态依然可以转移到存在这种维生素的状态。

我的转移判断大致是这样:当遍历到包含某种维生素的饮料时,没有这种维生素的情况能转移到有这种维生素的情况,有这种维生素的情况也能转移到有这种维生素的情况;而遍历到不包含某种维生素的饮料时,没有这种维生素的情况能转移到没有这种维生素的情况,有这种维生素的情况也能转移到有这种维生素的情况。

 1 #include<bits/stdc++.h>
 2 #define N 1001
 3 using namespace std;
 4 int inf;
 5 inline int read(){
 6     int x=0; bool f=1; char c=getchar();
 7     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 8     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 9     if(f) return x;
10     return 0-x;
11 }
12 int n,a,b[3],len,dp[2][2][2];//dp[i][j][k]
13 char c[5];
14 int main(){
15     memset(dp,0x7f,sizeof dp);
16     inf=dp[0][0][0];
17     dp[0][0][0]=0;
18     n=read();
19     int i,j,k,p;
20     for(i=1;i<=n;i++){
21         a=read(); scanf("%s",c);
22         len=strlen(c);
23         b[0]=b[1]=b[2]=0;
24         for(j=0;j<len;j++) b[c[j]-'A']=1;
25         for(j=0;j<2;j++)
26             for(k=0;k<2;k++)
27                 for(p=0;p<2;p++)
28                     if(j>=b[0] && k>=b[1] && p>=b[2]){ //对应维生素数量够才能转移 
29                         dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k-b[1]][p-b[2]]+a);
30                         if(b[0]) dp[j][k][p]=min(dp[j][k][p],dp[j][k-b[1]][p-b[2]]+a);
31                         if(b[1]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k][p-b[2]]+a);
32                         if(b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k-b[1]][p]+a);
33                         if(b[0] && b[1]) dp[j][k][p]=min(dp[j][k][p],dp[j][k][p-b[2]]+a);
34                         if(b[0] && b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j][k-b[1]][p]+a);
35                         if(b[1] && b[2]) dp[j][k][p]=min(dp[j][k][p],dp[j-b[0]][k][p]+a);//printf("7:%d\n",dp[j][k][p]);
36                     }
37     }
38     if(dp[1][1][1]==inf) printf("-1\n");
39     else printf("%d\n",dp[1][1][1]);
40     return 0;
41 }
View Code

T3:Array Product

题意:一个序列有$n$个数。你可以合并任意两个数,即先删除两个数,再将两数相乘的值放到右边那个数的位置;同时你还有$1$次机会直接删除序列中的一个数。问做$n-1$次前两种操作后序列中剩下的唯一的数最大可以是多少。

题解:

找规律题,很容易发现对于普通的合并操作,如果合并的数都不变,最终相乘合并的结果与合并顺序无关。而只有“直接删除一个数”操作能修改一次合并的数,于是这个操作怎么用就是关键。

首先,如果只用合并操作,在序列中有奇数个负数的情况下,最终乘积是负数。删去最大(绝对值最小)的一个负数,即让剩下的数的乘积变为正数 且 让所有负数的乘积为正数并最大 肯定更优;

其次,如果只用合并操作,在序列中存在0的情况下,最终乘积是0。删去这些0,让剩下的数的乘积变为正数 肯定更优。

但是第二种情况如何删除所有的0?另外上面两种情况同时存在怎么办?

考虑合并解决这些不需要的数,把这些要删除的数都相乘合并起来,然后一次删除它们合并出的数。这样就完美利用合并操作解决了只能进行一次删除操作的限制。

注意只有一个负数、全是0等极端情况,这些情况下千万不要多输出一步操作(比如序列的n个数都是0,你合并了n-1次0后又删了一次0,总共变成了n次)。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define INF 2147483647
 4 #define MAXN 100+100
 5 #define rep(i,s,t) for(int i=s;i<=t;++i)
 6 #define dwn(i,s,t) for(int i=s;i>=t;--i)
 7 
 8 using namespace std;
 9 
10 const int maxn=1000000+1010;
11 
12 inline int read(){
13     int x=0,f=1;char ch=getchar();
14     while(!isdigit(ch)&&ch!='-')ch=getchar();
15     if(ch=='-')f=-1,ch=getchar();
16     while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
17     return x*f;
18 }
19 
20 inline void write(int x){
21     int f=0;char ch[20];
22     if(!x){putchar('0'),putchar('\n');return;}
23     if(x<0)x=-x,putchar('-');
24     while(x)ch[++f]=x%10+'0',x/=10;
25     while(f)putchar(ch[f--]);
26     putchar('\n');
27 }
28 
29 int n,a[maxn],tot,l[maxn],tot1,xy0=-2147483646,xy1=-1;
30 bool book[maxn];
31 int main(){
32     n=read();
33     for(int i=1;i<=n;i++){
34        a[i]=read();
35        if(a[i]==0)l[++tot]=i;
36        if(a[i]<0){
37                tot1++;
38            if(xy0<a[i])xy0=a[i],xy1=i;
39        }
40     }
41     int m=n;
42     if(n==1)return 0;
43     if(tot1%2==1){
44         if(tot>=1)printf("1 %d %d\n",xy1,l[1]);
45         else {
46             printf("2 %d\n",xy1);
47         } n--;
48         book[xy1]=1;
49         if(n==1)return 0;
50     }
51     for(int i=1;i<tot;i++){
52         printf("1 %d %d\n",l[i],l[i+1]);    
53         book[l[i]]=1; n--;
54         if(n<=1)return 0;
55     }
56     if(tot>=1){n--;book[l[tot]]=1;printf("2 %d\n",l[tot]);}
57     if(n<=1)return 0; queue<int>q;
58     for(int i=1;i<=m;i++)if(!book[i])q.push(i);
59     while(!q.empty()){
60         if(n<=1)return 0;
61         int u=q.front();
62         q.pop();
63         n--;
64         int x1=q.front();
65         printf("1 %d %d\n",u,x1);
66     }
67     return 0;
68 }
View Code

 

T4:Petya and Array

题意:一个序列有$n(n \leq 200000)$个数,问这个序列中总共有多少个和小于等于$t(t \leq 10^{14})$。

题解:

由于负数的存在,不能直接滑动窗口,因此这就是一道裸的数据结构题了。

设$sum[i]$表示第1到i个数的和,则题意转化为满足$sum[r]-sum[l-1]<=t | 1<=l<=r$的个数。

移项得到$sum[r]-t<=sum[l-1]$。

目前我知道3种方法:

1、树状数组,将上式看做为求逆序对,找前$i-1$个前缀中小于等于$sum[r]-t$的前缀数量。但是开$10^{14}$以上大小的树状数组不现实,因此把$n$个前缀和离散化后插入$n$大小的树状数组即可。

2、平衡树,同上,求$sum[r]-t$在前$i-1$个前缀和中从小到大的排名(其实也是求满足条件的前缀数量)。平衡树的每个点都代表一个前缀和,因此最多只有$n$个点。

3、值域线段树,由于有$10^{14}$的大小,因此要动态开点,粗略计算最多建$200000*log2(10^{14})$个点,大约1000w个点吧。算上常数,这个做法时间不太稳,可能超时。(没试)

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define maxn 1000005
 4 using namespace std;
 5 inline ll read(){
 6     ll x=0; bool f=1; char c=getchar();
 7     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 8     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 9     if(f) return x;
10     return 0-x;
11 }
12 
13 int n;
14 ll t;
15 namespace Treap{
16     int rnd[maxn],cnt[maxn];
17     int rt,size,lch[maxn],rch[maxn],big[maxn];
18     ll num[maxn];
19     void init(){rt=size=0; return;}
20     
21     inline void update(int u){
22         big[u] = big[lch[u]]+big[rch[u]]+cnt[u];
23         return;
24     }
25     
26     inline void lturn(int &u){
27         ll t=rch[u];
28         rch[u]=lch[t], lch[t]=u;
29         big[t]=big[u], update(u), u=t;
30         return;
31     }
32     
33     inline void rturn(int &u){
34         ll t=lch[u];
35         lch[u]=rch[t], rch[t]=u;
36         big[t]=big[u], update(u), u=t;
37         return;
38     }
39     
40     void insert(int &cur,ll x){
41         if(cur==0){
42             cur=++size, num[cur]=x;
43             cnt[cur]=1, rnd[cur]=rand();
44             big[cur]=1; return;
45         }
46         big[cur]++; 
47         if(num[cur]==x)cnt[cur]++;
48         else if(x>num[cur]){
49             insert(rch[cur],x);
50             if(rnd[rch[cur]]<rnd[cur]) lturn(cur);
51         }
52         else{
53             insert(lch[cur],x);
54             if(rnd[lch[cur]]<rnd[cur]) rturn(cur);
55         }
56         return;
57     }
58     
59     void del(int &k,ll x){
60         if(k==0) return;
61         else if(num[k]==x){
62             if(cnt[k]>1) {cnt[k]--,big[k]--; return;}
63             if(lch[k]*rch[k]==0) {k=lch[k]+rch[k]; return;}
64             if(rnd[lch[k]]>rnd[rch[k]]) lturn(k), del(lch[k],x);
65             else rturn(k), del(rch[k],x);
66         }
67         else if(num[k]>x) big[k]--, del(lch[k],x);
68         else big[k]--, del(rch[k],x);
69         update(k);
70         return;
71     }
72     
73     int query(int cur,ll x){
74         if(cur==0) return 0;
75         if(num[cur]<=x) return query(rch[cur],x);
76         else return big[cur]-big[lch[cur]]+query(lch[cur],x);
77     }
78 }
79 using namespace Treap;
80 
81 int main(){
82     init(); ll ans=0;
83     n=read(),t=read();
84     insert(rt,0);
85     for(ll i=1,s=0;i<=n;i++){
86         s+=read();
87         ans+=query(rt,s-t);
88         insert(rt,s);
89     }
90     printf("%I64d\n",ans);
91     return 0;
92 }
平衡树(Treap)

 

posted @ 2018-09-17 21:08  大本营  阅读(234)  评论(0)    收藏  举报