前言
昨夜打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;
}