【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);

		}

	}

}

  

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

你可能感兴趣的:(codeforces)