Codeforces Round #439 (Div. 2)题解
Codeforces Round #439 (Div. 2)题解
A. Bark to Unlock
题意:
输入一些两个字符的串,问能否通过拼接(可以拼接自己),使拼接出的串含有某个两字符的子串
思路:
暴力枚举
AC代码:
#include <bits/stdc++.h>
using namespace std;
string pass,bark[102];
int n;
bool judge()
{
for (int i=1;i<=n;i++)
if(bark[i]==pass)
return true;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
string tmp;
tmp.push_back(bark[i][1]);
tmp.push_back(bark[j][0]);
if(tmp==pass)
return true;
}
return false;
}
int main()
{
cin>>pass;
scanf("%d",&n);
for (int i=1;i<=n;i++)
cin>>bark[i];
if(judge())
puts("YES");
else
puts("NO");
return 0;
}
B. Race Against Time
题意:
给定一个钟表的状态和两个时间点,钟表被时分秒三个表针分成了三个区域,问这两个时间点是否在同一个区域
思路:
用double
模拟就行了,判断细致一些
AC代码:
#include <bits/stdc++.h>
using namespace std;
int h,m,s,tt1,tt2;
double t1,t2;
double c[5];
bool judge()
{
for (int i=1;i<=2;i++)
if(t1>c[i]&&t1<c[i+1]&&t2>c[i]&&t2<c[i+1])
return true;
if(t1<c[1]&&t2<c[1]) return true;
if(t1>c[3]&&t2>c[3]) return true;
if(t1>c[3]&&t2<c[1]) return true;
if(t1<c[1]&&t2>c[3]) return true;
return false;
}
int main()
{
scanf("%d%d%d%d%d",&h,&m,&s,&tt1,&tt2);
h%=12;
t1=tt1*5.0,t2=tt2*5.0;
c[1]=(h+m*1.0/60+s*1.0/3600)*5;
c[2]=m+s*1.0/60;
c[3]=s;
sort(c+1,c+4);
//cout<<c[1]<<" "<<c[2]<<" "<<c[3]<<" "<<t1<<" "<<t2<<endl;
if(judge())
puts("YES");
else
puts("NO");
}
C. Qualification Rounds
题意:
有若干个队伍(\(k \le 4\)),和一个题库($ n \le 100000$),每个队伍认识一些题,问能否找到一个出题方案,使得每个队伍最多认识一半的题目
思路:
把认识题目的情况状压,如果存在一对且起来为0,那么存在,反之不存在
证明:
这一对显然满足条件;如果不存在,那么必然存在一个队伍认识所有的题,故此时无解。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n,k;
const int maxn=1e5+7;
bool vis[50];
bool solve()
{
if(vis[0]) return true;
for (int i=1;i<20;i++)
{
if(!vis[i]) continue;
for (int j=1;j<20;j++)
if((j&i)==0&&vis[j]) return true;
}
return false;
}
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
{
int tmp=0;
for (int i=0;i<k;i++)
{
int b;scanf("%d",&b);
tmp<<=1;
tmp|=b;
}
vis[tmp]=true;
}
if(solve())
puts("YES");
else
puts("NO");
}
D.Huge Strings
题意:
给定若干个\(01\)串集合(\(\sum |S| \le 100\)),和\(q\)次操作,每次将集合中的两个串的拼接加入这个集合中,同时输出这个新串含有的最大\(k\) ,\(2^k\)种不同的长度为\(k\)的\(01\)串全部为新串的子串
思路:
emmm……,这道题由于我错误地估计了上界导致没有做出来
评论区说,对于合并好的长度为\(k\)的串,最多产生\(k\)个新的子串,最后能证明答案的上界小于等于\(10\)
具体怎么证我还没有想好
先放一个我自己想的做法,由于长度大于\(10\)的串子串都是没用的,那么设左串是\(l\) ,右串是\(r\) ,首先预处理出每个串含有的长度小于\(10\)的所有子串;对于合并,先将\(l\) 和\(r\)的状态转移过去,然后搜索\(l\) 的后\(10\)个和\(r\)的前10个位组成的串产生的新子串,存储状态加以判断,最后在串集加入\(l\)的前十位和\(r\)的后十位组成的串。
然后看到了神奇的做法,对于合并长度大于一定长度的串,直接掐头去尾保留相应的长度,然后搜就行了,我并不认可这个做法,我觉得是数据出水了……
AC代码:
#include <bits/stdc++.h>
using namespace std;
string s[205];
bool vis[205][12][1200];
int n,q;
int solve(int l,int r,int cur)
{
string tmp;
if(r!=cur)
tmp=s[l].substr(max(0,(int)s[l].size()-11),s[l].size())+s[r].substr(0,min((int)s[r].size(),11));
else
tmp=s[r];
for (int i=10;i>=1;i--)
{
for (int j=0;j<(1<<i);j++)
{
vis[cur][i][j]=vis[l][i][j]|vis[r][i][j];
string t="";
for (int k=0;k<i;k++)
if(j&(1<<k)) t+='1';
else t+='0';
if(tmp.find(t)!=string::npos) {
vis[cur][i][j]=true;
//cout<<t<<endl;
}
}
bool flag=true;
for (int j=0;j<(1<<i);j++)
if(!vis[cur][i][j])
{
flag=false;
break;
}
if(flag) return i;
}
return 0;
}
int main()
{
scanf("%d",&n);
s[0]="";
for (int i=1;i<=n;i++)
{
cin>>s[i];
solve(0,i,i);
}
scanf("%d",&q);
for (int i=1;i<=q;i++)
{
int l,r;scanf("%d%d",&l,&r);
int cur=i+n;
s[cur]=s[l]+s[r];
if(s[cur].size()>22) s[cur]=s[cur].substr(0,11)+s[cur].substr(s[cur].size()-11,s[cur].size());
cout<<solve(l,r,cur)<<endl;
}
}