CF1287 div2题解

前言

昨夜打CF div2,思涨分之事。然脑未上线,BC题皆挂,仅A两道。
特写此篇,以记此耻。

所有题题面:https://codeforces.com/contest/1287/problems

A. Angry Students

题面:https://codeforces.com/contest/1287/problem/A
题解:直接扫一遍,记录\(A\)后面最长的一段\(P\)即可。
时间复杂度:\(O(n)\)
代码:略

B. Hyperset

题面:https://codeforces.com/contest/1287/problem/B
题解:
可以发现对于任意一对卡,能和它们组成一组的卡片是唯一的。
暴力枚举所有卡对,在map里存储信息,直接查找即可。
时间复杂度:O(\(n^2\)klogn)
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
map<string,int>mp;
string s;
char c[2020][110],t[110];
int n,m;
ll ans;
I Match(int x,int y){
	F(i,1,m){
		if(c[x][i]==c[y][i])t[i]=c[x][i];
		else {
			if(c[x][i]=='S'&&c[y][i]=='E')t[i]='T';
			else if(c[x][i]=='S'&&c[y][i]=='T')t[i]='E';
			else if(c[x][i]=='E'&&c[y][i]=='S')t[i]='T';
			else if(c[x][i]=='E'&&c[y][i]=='T')t[i]='S';
			else if(c[x][i]=='T'&&c[y][i]=='S')t[i]='E';
			else t[i]='S';
		}
	}
	s.clear();s.append(t+1);
}
int main(){
	//cin>>t+1;s.append(t+1);
	//F(i,0,s.size()-1)cout<<s[i];
	//return 0;
	read(n);read(m);
	F(i,1,n)cin>>c[i]+1;
	F(i,1,n-1){
		F(j,i+1,n){
			Match(i,j);
			if(!mp.count(s))continue;
			ans+=mp[s];
		}
		s.clear();s.append(c[i]+1);mp.insert(make_pair(s,1));
	}
	cout<<ans;
	return 0;
}

C. Garland

题面:https://codeforces.com/contest/1287/problem/C
题解:考虑DP。
\(f[i][j][k][0/1]\)表示考虑了前\(i\)位,已经在0的地方放了\(j\)个偶数
\(k\)个奇数的最小代价。转移方程分当前位是不是0讨论即可。
时间复杂度:O(\(n^3\))
这已经足够通过本题(我太菜了),考虑如何优化。
发现\(i\)确定时,\(j+k\)是个定值。所以可以把\(j,k\)挤进一个变量里。
时间复杂度:O(\(n^2\))
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
I read(int &res){
	res=0;re g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
int n,m,a[110],v[110],s[110],f[110][110][2],A,B;
int main(){
	read(n);
	F(i,1,n){
		read(a[i]);s[i]=s[i-1];
		if(!a[i])a[i]=-1,s[i]++;
		else v[a[i]]=1,a[i]&=1;
	}
	F(i,1,n){
		if(!v[i]){
			if(i&1)B++;else A++;
		}
	}
//	F(i,1,n)cout<<a[i]<<" ";
	//cout<<endl;
	C(f,63);
	f[0][0][0]=0;f[0][0][1]=0;
	if(a[1]!=-1){
		F(j,0,min(s[1],A))if(s[1]-j<=B)f[1][j][a[1]]=min(f[0][j][0],f[0][j][1]);
	}
	else{
		F(j,0,min(s[1],A)){
			if(s[1]-j>B)continue;
			if(j)f[1][j][0]=min(f[0][j-1][0],f[0][j-1][1]);
			f[1][j][1]=min(f[0][j][0],f[0][j][1]);
		}
	}
	F(i,2,n){
		if(a[i]!=-1){
			F(j,0,min(s[i],A))if(s[i]-j<=B)f[i][j][a[i]]=min(f[i-1][j][0]+a[i],f[i-1][j][1]+(a[i]^1));
			continue;
		}
		F(j,0,min(s[i],A)){
			if(s[i]-j>B)continue;
			if(j)f[i][j][0]=min(f[i-1][j-1][0],f[i-1][j-1][1]+1);
			f[i][j][1]=min(f[i-1][j][0]+1,f[i-1][j][1]);
		}
	}
	//F(i,1,n)F(j,0,min(s[i],A))cout<<i<<" "<<j<<" "<<f[i][j][0]<<" "<<f[i][j][1]<<endl;
	cout<<min(f[n][A][0],f[n][A][1]);
	return 0;
}

D. Numbers on Tree

题面:https://codeforces.com/contest/1287/problem/D
题解:考虑从下到上解决问题。
每次操作,我们可以把当前节点子树中的点的值都加进一个堆里,
找到分界位置把当前节点的\(a[i]\)赋值,并给其后面的对应点的值+1。
但这样做是不对的,因为有可能在一次操作中,这样的加操作可能会改变
之前子树内点的大小关系。考虑如何避免这个问题。
可以证明,如果有解,一定存在一种方案,使得所有点的\(a[i]\)都不一样。
因此,我们可以记录一下上次堆里的最大值,下次先给所有点的值加上这个最大值,再进堆。
时间复杂度:O(\(n^2\)logn)
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
I read(int &res){
	res=0;re g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
typedef pair<int,int>pii;
priority_queue<pii>q;
vector<int>e[2020];
int n,m,root,tot,bas,dep[2020],fa[2020],c[2020],w[2020];
pii b[2020];
I D_1(int x,int d){
	dep[x]=d;q.emplace(make_pair(d,x));
	for(auto p:e[x])D_1(p,d+1);
}
I D_2(int x){
	w[x]+=bas;
	b[++tot]=make_pair(w[x],x);
	for(auto p:e[x])D_2(p);
}
int main(){
	read(n);
	F(i,1,n)read(fa[i]),read(c[i]);
	F(i,1,n)if(!fa[i])root=i;else e[fa[i]].emplace_back(i);
	D_1(root,1);
	while(!q.empty()){
		m=q.top().second;q.pop();tot=bas=0;
		for(auto p:e[m]){
			D_2(p);sort(b+1,b+1+tot);bas=b[tot].first;
		}
		if(tot<c[m]){cout<<"NO";return 0;}
		sort(b+1,b+1+tot);w[m]=b[c[m]].first+1;
		F(i,c[m]+1,tot)w[b[i].second]++;
	}
	cout<<"YES"<<endl;
	F(i,1,n)cout<<w[i]<<" ";
	return 0;
}

E Madhouse

题面:https://codeforces.com/contest/1287/problem/D
题解:首先考虑简单版。
我们可以询问\([1,n]\)以及\([1,n-1]\)
发现第一次询问比第二次多了\(n\)个串,这\(n\)个串恰好是
这个串的\(n\)个后缀的乱序版本。找到这\(n\)个串即可还原整个串。
总串数:O(\(n^2\))
对于困难版,我们可以采用同样的方法,先求出\([1,n/2]\)这部分的串。
然后再询问\([1,n]\)这部分。
\(f[i][x]\)表示这次询问所有长度为i的串中,字符\(x\)的出现次数。
考虑\(f[i+1][x]-f[i][x]\)的意义,发现这个东西即为原串\([i+1,n-i]\)这段区间内
字符\(x\)的出现次数。
因为我们已经求出了前半段,后半段可以通过倒序枚举\(i\)逐个求出。
总串数:O(0.75*\(n^2\))
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
map<string,int>mp;
struct P{
	string a;
	friend bool operator > (P x,P y){
		return x.a.size()==y.a.size()?x.a.compare(y.a)>0:x.a.size()>y.a.size();
	}
}p;
priority_queue<P,vector<P>,greater<P> >q;
char c[110],ans[110];
string s;
int n,m,cnt[30],now[30],f[110][30];
namespace solve1{
	I solve(int x){
		if(x==1){
			cout<<"? 1 1"<<endl;fflush(stdout);
			cin>>c+1;ans[1]=c[1];return;
		}
		cout<<"? 1 "<<x<<endl;fflush(stdout);
		F(i,1,x*(x+1)/2){
			cin>>c+1;m=strlen(c+1);sort(c+1,c+1+m);p.a.clear();p.a.append(c+1);q.emplace(p);
		}
		cout<<"? 1 "<<x-1<<endl;fflush(stdout);
		F(i,1,x*(x-1)/2){
			cin>>c+1;m=strlen(c+1);sort(c+1,c+1+m);s.clear();s.append(c+1);mp[s]++;
		}
		C(cnt,0);m=0;
		while(!q.empty()){
			s=q.top().a;q.pop();//cout<<"@"<<s<<endl;
			if(mp.count(s)){
				if(mp[s]==1)mp.erase(s);
				else mp[s]--;
				continue;
			}
			C(now,0);
			F(i,0,s.size()-1){
				now[s[i]-'a'+1]++;
			}
			F(i,1,26)if(now[i]>cnt[i]){ans[++m]=i+'a'-1;cnt[i]++;break;}
		}
		reverse(ans+1,ans+1+x);
	}
};
namespace solve2{
	I solve(){
		cout<<"? 1 "<<n<<endl;fflush(stdout);
		F(i,1,n*(n+1)>>1){
			cin>>c+1;m=strlen(c+1);
			F(j,1,m)f[m][c[j]-'a'+1]++;
		}
		re l,r;
		FOR(i,(n-1)>>1,0){
			l=i+1;r=n-i;
			C(cnt,0);
			F(j,l,r-1)cnt[ans[j]-'a'+1]++;
			F(j,1,26)if(f[i+1][j]-f[i][j]>cnt[j]){ans[r]=j+'a'-1;break;}
		}
		cout<<"! "<<ans+1<<endl;fflush(stdout);
	}	
};
int main(){
	read(n);
	if(n==1){
		cout<<"? 1 1"<<endl;fflush(stdout);
		cin>>c+1;cout<<"! "<<c[1]<<endl;fflush(stdout);
		return 0;
	}
	solve1::solve(n/2);
	solve2::solve();
	return 0;
}
posted @ 2020-01-07 11:51  Purple_wzy  阅读(383)  评论(0编辑  收藏  举报