Codeforces Round #220 (Div. 2)

来源:互联网 发布:归来。知乎 编辑:程序博客网 时间:2024/06/06 09:27

这场单独的div2真的好难,trick太多。A题几乎写了的人都是错的,各种hack。。。B题出题人想错了,最后unrated了 。我B题错的和出题人一样。。。最后只过了C。。。


A:

把4个位移式子相互叠加一下可以推出类似(x,y-2b)的式子。

  • (x - a, y - b)
  • (x + a, y - b)

就是一个坐标可以不变,另一个坐标偏移2次。前提是x要有足够的空间上下各偏移一次。难么只要2个坐标变换次数相差偶数,就能调整成一样。满足条件后,答案取二者中偏移次数多的。枚举4个角即可。


code:

#include <algorithm>#include <iostream>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <string>#include <math.h>#include <vector>#include <queue>#include <stack>#include <cmath>#include <list>#include <set>#include <map>using namespace std;#define N  100010#define ALL(x)     x.begin(),x.end()#define CLR(x,a)   memset(x,a,sizeof(x))typedef long long  ll;typedef pair<int,int> PI;const int INF    = 0x3fffffff;const int MOD    = 100000007;const double EPS = 1e-7;bool move=true;int ans=INF;void solve(int x,int y){if(!move && (x||y)) return ;if(abs(x-y)%2) return ;ans=min(ans,max(x,y));}int main(){int n,m,i,j,a,b;scanf("%d%d%d%d%d%d",&n,&m,&i,&j,&a,&b);if( (i-1<a && n-i<a) || (j-1<b && m-j<b) ) move=false;if((i-1)%a==0 && (j-1)%b==0) solve((i-1)/a, (j-1)/b);if((i-1)%a==0 && (m-j)%b==0) solve((i-1)/a, (m-j)/b);if((n-i)%a==0 && (j-1)%b==0) solve((n-i)/a, (j-1)/b);if((n-i)%a==0 && (m-j)%b==0) solve((n-i)/a, (m-j)/b);if(ans==INF) puts("Poor Inna and pony!");else printf("%d\n",ans);return 0;}


B:

对于一段连续的区间[l,r],满足(digit[i]+dight[i+1])==9,(l<=i<r)。设长度为n。

那么这段区间内,变换成最多9的情况有多少种呢?

1.n为偶数,显然变成n/2个9,只有一种变法。

2.n为奇数,有(n+1)/2种变成最多9的变法。这里最开始我认为只有2种,即从左往右开始变,和从右往左开始变,出题人也是这么认为的。。。。然后过了pre我就没管了。。。事实上能忽略一个奇数位的数字,剩下的数字个数为偶数,能全用完变成9。那么答案就是奇数位数字的个数。


累乘每段区间的种数就是答案了。


code:

#include <algorithm>#include <iostream>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <string>#include <math.h>#include <vector>#include <queue>#include <stack>#include <cmath>#include <list>#include <set>#include <map>using namespace std;#define N  200010#define ALL(x)     x.begin(),x.end()#define CLR(x,a)   memset(x,a,sizeof(x))typedef long long    ll;typedef pair<int,int> PI;const int INF    = 0x3fffffff;const int MOD    = 100000007;const double EPS = 1e-7;char s[N];int dp[N];int main(){scanf("%s",s);for(int i=0;s[i];i++) s[i]-='0';int star=0;int n=strlen(s);ll ans=1;for(int i=0;i<n-1;i++){if(s[i]+s[i+1]!=9){if(i-star+1>2 && (i-star+1)%2 ) ans*=(i-star+2)/2;star=i+1;}}if(n-star>2 && (n-star)%2) ans*=(n-star+1)/2;printf("%I64d\n",ans);return 0;}



C:

可以记忆化搜索,dp[x][y]表示从这点出发,最多走出几个“DIMA”。不过比赛的时候,我第一反应是建图,写个拓扑排序。建图就是对于所有“DIMA”的路径,D到A连一条权值为1的边。然后所有A到D连一条权值为0的边,都是有向的。

下面代码是拓扑排序+dp的做法。


code:

#include <algorithm>#include <iostream>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <string>#include <math.h>#include <vector>#include <queue>#include <stack>#include <cmath>#include <list>#include <set>#include <map>using namespace std;#define N  1024#define ALL(x)     x.begin(),x.end()#define CLR(x,a)   memset(x,a,sizeof(x))typedef long long    ll;typedef pair<int,int> PI;const int INF    = 0x3fffffff;const int MOD    = 100000007;const double EPS = 1e-7;const int dx[]  = {-1, 1, 0, 0};const int dy[]  = {0, 0, -1, 1};int n,m,U;char c[4]={'D','I','M','A'};char mp[N][N];bool g[N][N];vector<PI> e[N*N];int has;bool vis[N*N];void dfs(int x,int y,int step){if(step==4){has++;vis[m*x+y]=true;e[U].push_back((PI){m*x+y,1});return ;}for(int i=0;i<4;i++){int xx=dx[i]+x;int yy=dy[i]+y;if(xx<0 || xx>=n || yy<0 || yy>=m) continue;if(mp[xx][yy]==c[step]){dfs(xx,yy,step+1);}}}int dp[N*N];int in[N*N];void topSort(){int num=0,cnt=0;for(int i=0;i<n*m;i++){if(vis[i]) cnt++;for(int j=0;j<e[i].size();j++)in[e[i][j].first]++;}queue<int> que;for(int i=0;i<n*m;i++){if(in[i]==0 && vis[i]) que.push(i);}while(!que.empty()){int u=que.front();que.pop();num++;for(int i=0;i<e[u].size();i++){int v=e[u][i].first;dp[v]=max(dp[v],dp[u]+e[u][i].second);if(--in[v]==0) que.push(v);}}if(num<cnt)puts("Poor Inna!");elseprintf("%d\n",*max_element(dp,dp+n*m));}int main(){scanf("%d%d",&n,&m);for(int i=0;i<n;i++) scanf("%s",mp[i]);for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(mp[i][j]=='D'){U=m*i+j;vis[U]=true;dfs(i,j,1);}for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(mp[i][j]=='A'){U=m*i+j;vis[U]=true;for(int k=0;k<4;k++){int xx=dx[k]+i;int yy=dy[k]+j;if(xx<0 || xx>=n || yy<0 || yy>=m) continue;if(mp[xx][yy]=='D'){vis[m*xx+yy]=true;e[U].push_back((PI){m*xx+yy,0});}}}if(has) topSort();else puts("Poor Dima!");return 0;}


D:

二分+树状数组

先把每次删除的k记录下来,然后把所有01元素按顺序都加到序列里,最后统一进行删除操作。

我们用1标记某个位置上的元素是否存在。如果sum(i)==p 且 sum(i)-sum(i-1)==1 那么说明,第i个元素目前是序列里的第p个1(也就是经过一些删除操作后,它现在排在第p个)。


对于删除操作,二分出要删除元素的位置,把它清零就好了。


code:

#include <algorithm>#include <iostream>#include <string.h>#include <stdlib.h>#include <stdio.h>#include <string>#include <math.h>#include <vector>#include <queue>#include <stack>#include <cmath>#include <list>#include <set>#include <map>using namespace std;#define N  1000010#define ALL(x)     x.begin(),x.end()#define CLR(x,a)   memset(x,a,sizeof(x))typedef long long  ll;typedef pair<int,int> PI;const int INF    = 0x3fffffff;const int MOD    = 1000000007;const double EPS = 1e-7;int a[N],c[N],d[N],t[N];int n,m,num;void add(int i,int x){for(;i<=num;i+= -i&i) c[i]+=x;}int sum(int i){if(i>num) return num+1;int ans=0;for(;i>=1;i-= -i&i) ans+=c[i];return ans;}void remove(int end){vector<int> pos;for(int i=1;i<=end;i++){int l=1,r=num+1;while(l<r){int mid=(l+r)>>1;int x=sum(mid);if(x<a[i] || (x==a[i] && sum(mid)-sum(mid-1)==1) ) l=mid+1;else r=mid;}pos.push_back(r-1);}for(int i=0;i<pos.size();i++) add(pos[i],-1);}int main(){int op,len=0,T=0;scanf("%d%d",&m,&n);for(int i=1;i<=n;i++) scanf("%d",a+i);while(m--){scanf("%d",&op);if(op==-1){int end=upper_bound(a+1,a+n+1,len)-a-1;len-=end;t[T++]=end;}else{num++;len++;d[num]=op;}}for(int i=1;i<=num;i++) add(i,1);for(int i=0;i<T;i++) remove(t[i]);bool can=true;for(int i=1;i<=num;i++) if(sum(i)-sum(i-1)==1){printf("%d",d[i]);   can=false;}if(can) puts("Poor stack!");return 0;}


0 0