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;
    }
}

F题待补

posted @ 2017-10-06 16:22  阿瓦隆的精灵  阅读(147)  评论(0编辑  收藏  举报