Codeforces Round #621 (Div. 1 + Div. 2)

A. Cow and Haybales

题意:t(100)组数据,n(100)个数,每次可以操作相邻的两个数,使得一个数+1,一个数-1,问d(100)次操作最多使得最左边的数为多少。

思路:贪心即可,从左往右枚举,能移过去多少就移过去多少。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=105;
17 using namespace std;
18 int T;
19 int n,d;
20 int a[N];
21 int main(){
22 // freopen("in.txt","r",stdin);
23  rd(T);
24  while(T--){
25   rd(n);rd(d);
26   for(int i=1;i<=n;i++)rd(a[i]);
27   for(int i=2;i<=n;i++){
28    if(d > (i-1)*a[i])a[1]+=a[i],d-=(i-1)*a[i];
29    else {a[1]+=d/(i-1);break;}
30   }
31   printf("%d\n",a[1]);
32  }
33  return 0;
34 }
35 /**/
View Code

B. Cow and Friend

题意:n(1e5)个数an,每次可以移动到一个距离当前点ai的点,问最少几次可以从(0,0)移动到(x,0)。

思路:如果an里面有x,那就是一次,不然就看最大的ai,如果大于x,就是2次,小于x的话就是最小的u使得u*mx>=x中的u个。很直观很容易证明。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=1e5+10;
17 using namespace std;
18 int T;
19 int n,x;
20 int a[N];
21 int main(){
22 // freopen("in.txt","r",stdin);
23  rd(T);
24  while(T--){
25   rd(n);rd(x);int mx=0,tag=0;
26   for(int i=1;i<=n;i++){
27    rd(a[i]);mx=max(mx,a[i]);
28    if(x == a[i])tag=1;
29   }
30   if(tag)printf("1\n");
31   else if(mx > x)printf("2\n");
32   else if(x % mx == 0)printf("%d\n",x/mx);
33   else printf("%d\n",x/mx+1);
34  }
35  return 0;
36 }
37 /**/
View Code

C. Cow and Message

题意:长度为n(1e5)的字符串,定义"等差子串"为选择的位置下标对应等差数列的子串,求出在等差子串中出现数目最多的子串出现了多少次。

思路:对于长度为1的等差子串,枚举每个字母即可。对于长度为二的等差子串,枚举两个字母是什么,再枚举n,就可以在26*26*n的时间复杂度内求出答案。对于长度大于等于三的等差子串,事实上它的前两位就已经将这个子串确定了下来,也就是说每一个长度大于等于三的等差子串都包含至少一个长度为二的等差子串,也就是长度大于等于三的等差子串不可能成为答案子串。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=1e5+10;
17 using namespace std;
18 int n;
19 char s[N];
20 LL ans;
21 int cnt[26],f[N][26];
22 int main(){
23 // freopen("in.txt","r",stdin);
24  scanf("%s",s);n=strlen(s);
25  for(int i=0;i<n;i++){
26   cnt[s[i]-'a']++;
27   if(i)for(int j=0;j<26;j++)f[i][j]=f[i-1][j];
28   f[i][s[i]-'a']++;
29  }
30  for(int i=0;i<26;i++){
31   ans=max(1ll*cnt[i],ans);
32   for(int j=0;j<26;j++){
33    LL tmp=0;
34    for(int k=1;k<n;k++)
35     if(s[k] == 'a'+j)tmp+=f[k-1][i];
36    ans=max(ans,tmp);
37   }
38  }
39  printf("%lld\n",ans);
40  return 0;
41 }
42 /**/
View Code

D. Cow and Fields

题意:n(2e5)点m(2e5)边无向图,有k(2e5)个特殊点,现在在k个特殊点之间修一条路,使得1~n的最短距离最大。

思路:容易想到先求1到所有点的距离和n到所有点的距离。这个用bfs就可以了,因为边权都是1,我当时抽风用了dijkstral。

接下来记录两个思路,我的思路和答案的思路还是有些不同的。

答案思路:假设xi表示i到1的距离,yi表示i到n的距离。那么就是要最大化min(xi+yj,xj+yi)+1,(这样看起来有些不严谨,因为还需要对原本1~n的距离取min,但后面会处理到这个问题,这里并不需要管),我们不妨认为xi+yj是较小的那个,那么可以得到ij满足xi-yi < xj-yj。那么就以此进行排序,同时维护后缀最大y值,枚举i并且通过后缀最大y值求得xi+yj+1的最大值。下面返回处理上面括号中的问题,容易当且仅当最终的max值大于原1~n的距离时才存在对1~n距离取min的意义,而只要存在这样的ij,最终答案一定就是原1~n的距离,所以只需要对最终答案和原1~n的距离取min即可。

我的思路:容易发现如果最短路通过新加的路,一定是从xi小的向xi大的移动,也就是i和1连接,j和n连接,这个画图就容易发现。所以维护一个set,以yi为关键字(最好以id为第二关键字不然删除的时候需要注意运用的是哪种删除方式),按xi从小到大枚举,依次删除后在set中lower_bound找到xi+1+yj小于等于1~n原距离的最大值即可,再判断最大的yj是否能使得路径大于原1~n距离,能的话答案就是原1~n的距离,直接break。

然而,这只是我考试时的思路,现在发现完全是多此一举,有很多没必要的操作,完全不需要二分,直接用最大值最后对原1~n距离取min即可,原理和答案方法相同。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=2e5+10;
17 using namespace std;
18 int n,m,k,a[N];
19 int fi[N],nxt[N<<1],to[N<<1],tot;
20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;}
21 struct Dijk{
22  int id,val;
23  bool operator < (const Dijk o)const{
24   return val > o.val;
25  }
26 };
27 priority_queue<Dijk>S;
28 struct ghb{
29  int id,dis1,disn;
30  bool operator < (const ghb o)const{
31   if(disn == o.disn)return id < o.id;
32   return disn > o.disn;
33  }
34 }f[N];
35 int dis[N];
36 bool vis[N];
37 void work1(){
38  memset(dis,0x3f,sizeof(dis));
39  memset(vis,0,sizeof(vis));
40  dis[1]=0;S.push((Dijk){1,0});
41  while(!S.empty()){
42   Dijk u=S.top();S.pop();if(vis[u.id])continue;vis[u.id]=1;
43   for(int i=fi[u.id];i;i=nxt[i])
44    if(dis[to[i]] > u.val+1){
45     S.push((Dijk){to[i],u.val+1});
46     dis[to[i]]=u.val+1;
47    }
48  }int tmp=0;
49  for(int i=1;i<=n;i++)if(a[i])f[++tmp].id=i,f[tmp].dis1=dis[i];
50 }
51 void work2(){
52  memset(dis,0x3f,sizeof(dis));
53  memset(vis,0,sizeof(vis));
54  dis[n]=0;S.push((Dijk){n,0});
55  while(!S.empty()){
56   Dijk u=S.top();S.pop();if(vis[u.id])continue;vis[u.id]=1;
57   for(int i=fi[u.id];i;i=nxt[i])
58    if(dis[to[i]] > u.val+1){
59     S.push((Dijk){to[i],u.val+1});
60     dis[to[i]]=u.val+1;
61    }
62  }int tmp=0;
63  for(int i=1;i<=n;i++)if(a[i])f[++tmp].disn=dis[i];
64 }
65 set<ghb>T;
66 bool cmp(ghb a,ghb b){return a.dis1 < b.dis1;}
67 int ans;
68 int main(){
69 // freopen("in.txt","r",stdin);
70  rd(n);rd(m);rd(k);
71  for(int i=1;i<=k;i++){int x;rd(x);a[x]=1;}
72  for(int i=1;i<=m;i++){
73   int x,y;rd(x);rd(y);
74   link(x,y);link(y,x);
75  }
76  work1();work2();
77  for(int i=1;i<=k;i++)T.insert(f[i]);
78  sort(f+1,f+k+1,cmp);
79  for(int i=1;i<k;i++){
80   T.erase(f[i]);int tmp=dis[1]-f[i].dis1-1;
81   set<ghb>::iterator it=T.lower_bound((ghb){0,0,tmp});
82   if(it != T.end())ans=max(ans,(*it).disn+f[i].dis1+1);
83   it=T.begin();if(it->disn + f[i].dis1 + 1 > dis[1]){ans=dis[1];break;}
84  }
85  printf("%d\n",ans);
86  return 0;
87 }
88 /**/
89  
View Code

反思:考试的时候没有考虑到底是谁连向谁,而是同时考虑两种情况,以为简单的汇总就可以,事实上完全不可以。遇到这种新加路更新最短路的情况,不妨研究到底是从谁走向谁。

E. Cow and Treats

题意:n(5000)堆草,m(5000)头牛,每堆草都有一个美味值,每个牛只吃对应美味值为f的草,且能吃h堆。每次你可以把一头牛从最左边放下或者从最右边放下,他将沿着草吃掉喜欢吃的,吃完后将在原地睡觉,在其睡觉前如果遇到了已经睡觉的牛或者走到了头,那么他会很生气。问最多能使多少头牛吃饱睡着,方案数有多少。(没有牛生气,并不需要喂所有的牛)

思路:答案竟然是nlog的...虽然我没看懂,不过和数据很不符,我的算法是n^2log的。

首先,如果我们把牛分为了两组,左边的和右边的,那么他们的吃草顺序一定是唯一的,很容易证明。同时,每一边一种美味度最多对应一头牛,如果有两头那么第二头一定会生气。两边的牛不能"相交",即左边的牛走的最右面不能在右边的牛走的最左边的右边。因为分好组之后吃草顺序是唯一的了,所以每种美味度对应的牛可以单独考虑,即单独考虑左面放谁,右面放谁。为了不重复统计答案,我们枚举左面的牛到达的最右面的点是哪个。需要注意一边可以不放牛的。预处理各种美味度对应草的堆数的前缀和。整体做法是,枚举断点,然后枚举每种美味度,每种美味度对应的可放在左面的牛二分出有多少头可以放在右面。统计出每种美味度的情况之后汇总为该断点的答案,然后对所有断点的答案进行汇总即可。

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 #define dl double
 4 void rd(int &x){
 5  x=0;int f=1;char ch=getchar();
 6  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 7  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
 8 }
 9 void lrd(LL &x){
10  x=0;int f=1;char ch=getchar();
11  while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12  while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f;
13 }
14 const int INF=1e9;
15 const LL LINF=1e18;
16 const int N=5e3+10;
17 const int mod=1e9+7;
18 using namespace std;
19 int n,m;
20 int s[N],cnt[N][N];
21 int f[N],h[N];
22 vector<int>S[N];
23 int ANS1,ANS2; 
24 int main(){
25 // freopen("in.txt","r",stdin);
26  rd(n);rd(m);
27  for(int i=1;i<=n;i++){
28   rd(s[i]);
29   for(int j=1;j<=n;j++)cnt[i][j]=cnt[i-1][j];
30   cnt[i][s[i]]++;
31  }
32  for(int i=1;i<=m;i++){
33   rd(f[i]);rd(h[i]);
34   S[f[i]].push_back(h[i]);
35  }
36  for(int i=1;i<=n;i++)sort(S[i].begin(),S[i].end());
37  ANS1=0,ANS2=1;
38  for(int i=0;i<=n;i++){
39   if(i)if(!binary_search(S[s[i]].begin(),S[s[i]].end(),cnt[i][s[i]]))continue;
40   int ans1=0,ans2=1;
41   int tmp=upper_bound(S[s[i]].begin(),S[s[i]].end(),cnt[n][s[i]]-cnt[i][s[i]]) - S[s[i]].begin() - 1;
42   if(cnt[i][s[i]] <= cnt[n][s[i]]-cnt[i][s[i]])tmp--;
43   if(i){if(tmp >= 0)ans1+=2,ans2=1ll*ans2*(tmp+1)%mod;else ans1++;}
44   for(int j=1;j<=n;j++){
45    if(j == s[i])continue;
46    int siz=S[j].size();
47    if(!siz)continue;
48    int tmp1=0,tmp2=1;
49    for(int l=0,r=siz-1;l < siz && S[j][l] <= cnt[i][j];l++){//不加l < siz不对 
50     while(r >= 0 && S[j][r] > cnt[n][j]-cnt[i][j])r--;
51     int tmp=r+1;
52     if(l <= r)tmp--;
53     if(tmp){
54      if(tmp1 == 2)tmp2=(tmp2+tmp)%mod;
55      else tmp1=2,tmp2=tmp;
56     }
57     else if(tmp1 != 2){
58      if(!tmp1)tmp1=1,tmp2=1;
59      else tmp2++;
60     }
61    }
62    int tmp=upper_bound(S[j].begin(),S[j].end(),cnt[n][j]-cnt[i][j]) - S[j].begin() - 1;
63    if(tmp1 == 1)tmp2+=tmp+1;
64    else if(!tmp1 && tmp >= 0){tmp1=1;tmp2=tmp+1;}
65    ans1+=tmp1;ans2=1ll*ans2*tmp2%mod;
66   }
67   if(ANS1 == ans1){if(ANS1)ANS2=(ANS2+ans2)%mod;}//0的情况不能累加 
68   else if(ANS1 < ans1){ANS1=ans1;ANS2=ans2;}
69  }
70  printf("%d %d\n",ANS1,ANS2);
71  return 0;
72 }
73 /*
74 1.组分好了之后吃的顺序是唯一的。
75 2.吃一种味道的草的牛一边最多有一个 
76 3.吃同一种味道草的牛在两边可以随意换
77 4.吃味道不同的草的牛可以单独考虑
78 5.我觉得我做出来这道题了
79 6.我的思路并不完善,有问题,必须保证两边的牛不相交才可以,也是,想想复杂度也是假算法
80 7.不过我觉得枚举一下分界点仍然可以轻松解决 
81 lower_bound和upper_bound就可以实现四种找的操作了,不需要重载 
82 */
View Code

反思:枚举断点使得答案统计不重复这个思路需要记住。

posted @ 2020-03-02 20:20  hyghb  阅读(224)  评论(0编辑  收藏  举报