Codeforces Round #220 (Div. 2)

这场单独的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!");
	else
		printf("%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
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值