【Codeforces #135 Div2】Solutions

  English tutorial has been published.You can see here http://codeforces.com/blog/entry/5160

  

【A k-String】

  http://www.codeforces.com/contest/219/problem/A

  题目大意:给你一堆字母,问用这些字母能不能组成一个由某个循环节循环k次得到的字符串。

  显然我们可以从循环节入手,每个字母出现次数cnt[x]一定是k的整数倍,这样的话每个循环节里就有cnt[x]/k个当前字母。造好循环节之后输出k次就可以了。

#include <iostream>
#include <cstring>
#include <string>
using namespace std;

char s[2010],ch;
string res;
int n,a[50];

int main(){
	cin>>n>>s;
	int len=strlen(s);
	for(int i=0;i<len;i++){
		a[s[i]-'a']++;
	}
	if(len<n){
		cout<<-1<<endl;
		return 0;
	}
	for(int i=0;i<26;i++){
		if(a[i] && a[i]%n!=0){
			cout<<-1<<endl;
			return 0;
		}
	}
	for(int i=0;i<26;i++){
		if(a[i]){
			for(int j=0;j<(a[i]/n);j++){
				ch=i+'a';
				res=res+ch;
			}
		}
	}
	for(int i=1;i<=n;i++) cout<<res;
	cout<<endl;
}

  

【B Special Offer! Super Price 999 Bourles!】

  http://www.codeforces.com/contest/219/problem/B

  题目大意:一个数字p,在最多减少d的情况下,p-x末尾数字'9'的个数最多(0≤x≤d)。

  这个题的方向是,我们尽量让p的末尾0最多,然后减去一个1,后面的0就都变成9了。

#include <iostream>
typedef long long ll;
using namespace std;
ll p,d,ans;
int main(){
	cin>>p>>d;
	ans=++p;
	for(ll t=10;;t*=10){
		if(p%t>d) break;
		ans=p-p%t;
	}
	cout<<ans-1<<endl;
}

  

【C  Color Stripe】

  http://www.codeforces.com/contest/219/problem/C

  题目大意:有n个格子排成一排,有k种颜色,每个格子已经涂了颜色,问多少修改多少个格子的颜色,使得相邻格子颜色不同。

  当k=2时,确定第一个格子的颜色,涂色方案就唯一确定,于是可以枚举判断一下。

  当k>2时,如果连续一段格子颜色相同,我们显然要对其进行修改。假设区间为[l,r],长度len=r-l+1。当len为奇数时,显然修改{l+1,l+3,l+5...}最优;当长度为偶数时,修改{l,l+2,l+4...}或{l+1,l+3,l+5...}都能保证最优,为了与len为奇数时统一便于coding,我们选后一种修改方法。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

int n,k,ans;
char s[1000010];

int main(){
	cin>>n>>k;
	cin>>s;
	if(k==2){
		for(int i=0;i<n;i++)
			if((s[i]-'A')!=i%2) ans++;
		if(ans>n-ans){
			cout<<n-ans<<endl;
			for(int i=0;i<n;i++)
				printf("%c",(i+1)%2+'A');
				cout<<endl;
		}else{
			cout<<ans<<endl;
			for(int i=0;i<n;i++)
				printf("%c",i%2+'A');
				cout<<endl;
		}
		return 0;
	}
	int i=0,last=0;
	while(i<n){
		while(s[i]==s[last]) i++;
		if(i-last>1){
			for(int j=last+1;j<i;j+=2){
				for(int k=0;k<3;k++)
					if(s[j-1]!=k+'A' && s[j+1]!=k+'A'){
						ans++;
						s[j]=k+'A';
						break;
					}
			}
		}
		last=i;
	}
	cout<<ans<<endl;
	cout<<s<<endl;
}

  

【D Choosing Capital for Treeland】

  http://www.codeforces.com/contest/219/problem/D

  题目大意:一棵有向树,若选取x为“首都”,那么需要将一些边反向使得x到任意点可达。问最少需要将多少边反向,有那些点满足最少的要求。

  假定1为根。首先dfs处理出1为“首都”时需要反向的边数。

  第二次dfs:假设u是v的父节点,且u的答案已经得到,那么如果v是“首都”,除了从v到u的边需要改变方向之外,从v到v的子节点、从v到u的其他孩子的子节点都是与u为“首都”时相同,所以只需要考虑(u--v)这条边的方向。

#include <iostream>
#include <vector>
#define inf 2147483647
using namespace std;
template<class T>inline void gmin(T &a,T b){if(a>b)a=b;}

vector<int> ans_seq;
int n,a,b,ans=inf,cnt[200010];
int sum,med,tot;
bool vis[200010];

struct EDGE{
	int pnt,dist;
	EDGE *pre;
	EDGE(){}
	EDGE(int _pnt,int _dist,EDGE *_pre):dist(_dist),pnt(_pnt),pre(_pre){}
}Edge[400010],*SP=Edge,*edge[200010];

inline void addedge(int a,int b){
	edge[a]=new(++SP)EDGE(b,0,edge[a]);
	edge[b]=new(++SP)EDGE(a,1,edge[b]);
}

void dfs1(int x){
	vis[x]=true;
	for(EDGE *j=edge[x];j;j=j->pre)
		if(!vis[j->pnt]){
			dfs1(j->pnt);
			cnt[x]+=j->dist+cnt[j->pnt];
		}
}

void dfs2(int x){
	vis[x]=false;
	gmin(ans,cnt[x]);
	for(EDGE *j=edge[x];j;j=j->pre)
		if(vis[j->pnt]){
			cnt[j->pnt]=j->dist?(cnt[x]-1):(cnt[x]+1);
			dfs2(j->pnt);
		}
}

int main(){
	cin>>n;
	for(int i=1;i<n;i++){
		cin>>a>>b;
		addedge(a,b);
	}
	dfs1(1);
	dfs2(1);
	for(int i=1;i<=n;i++)
		if(ans==cnt[i]) ans_seq.push_back(i);
	cout<<ans<<endl;
	for(int i=0;i<ans_seq.size()-1;i++)
		cout<<ans_seq[i]<<" ";
	cout<<ans_seq[ans_seq.size()-1]<<endl;
}

  

【E  Parking Lot】

  http://www.codeforces.com/contest/219/problem/E

  题目大意:线性排列的一个停车场,依次有车开来或开走。若停车场没有车,那么就安排在1号车位;若已经有车,则要让安排的车位与已经停在这里的车的最小距离最大。输出安排序列。

  看到这个题就有思路,可以用线段树维护一个最长的连续全零序列,每次让车停在序列里,有点像“小白逛公园”……结果写了20分钟之后发现越写越恶心,懒标记、每次修改的维护、维护的数据之多(左起、中起、右起最长序列和各自分配的位置……),而且还有左右边界的特判……果断关机睡觉去了……

  今天看别人代码发现有用stl维护线段的方法,方便很多。

  与线段树一样,维护的是一个空白的连续线段,每个线段有长度(距离)、起点、分配位置三个值,放到set里以长度为关键字排序,从大向小选取即可。

  使用map也是可以实现的,也许修改会更方便。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <vector>
#include <utility>
#include <cmath>
#include <string>
#include <map>
#include <set>
#define inf 2147483647
using namespace std;
template<class T>inline void gmax(T &a,T b){if(a<b)a=b;}
template<class T>inline void gmin(T &a,T b){if(a>b)a=b;}
typedef pair<int,int> PII;
typedef long long LL;

int n,m,sym,id,lot[1000010],L[200010],R[200010];

struct SEGMENT{
	int len,st,pos;
}cur;

struct cmp{
	bool operator()(const SEGMENT a,SEGMENT b){
		if(a.len==b.len) return a.pos<b.pos;
		else return a.len>b.len;
	}
};

void match(int x,int y){L[x]=y,R[y]=x;}

set<SEGMENT,cmp> seg;

SEGMENT Make_seg(int left,int right){
	SEGMENT res;
	if(!left && right==n+1) res.len=inf,res.st=0,res.pos=1;
	else if(!left) res.len=right-1,res.st=0,res.pos=1;
	else if(right==n+1) res.len=n-left,res.st=left,res.pos=n;
	else res.len=(right-left)>>1,res.st=left,res.pos=left+res.len;
	return res;
}

void Insert_seg(int left,int right){
	L[right]=left,R[left]=right;
	seg.insert(Make_seg(left,right));
}

void Del_seg(int left,int right){
	seg.erase(Make_seg(left,right));
}

int main(){
	cin>>n>>m;
	Insert_seg(0,n+1);
	while(m--){
		cin>>sym>>id;
		if(sym==1){
			cur=*seg.begin();
			seg.erase(seg.begin());
			lot[id]=cur.pos;
			int pnt1=cur.st,pnt2=cur.pos,pnt3=R[pnt1];
			Insert_seg(pnt1,pnt2);
			Insert_seg(pnt2,pnt3);
			cout<<lot[id]<<endl;
		}else{
			int pnt1=L[lot[id]],pnt2=lot[id],pnt3=R[pnt2];
			Del_seg(pnt1,pnt2);
			Del_seg(pnt2,pnt3);
			Insert_seg(pnt1,pnt3);
		}
	}
}

  

【原创博文,转载请注明出处,谢谢】

posted @ 2012-08-28 13:00  Delostik  阅读(453)  评论(0编辑  收藏  举报