codeforces 1303 题解(完结)

codeforces 1303 题解

A. Erasing Zeroes

想让字符串中的 \(1\) 连续,而我们能做的只有删 \(0\) ,则需要删去除开头以及结尾外的 所有 \(0\) 块。所以从头扫一遍统计开头 \(0\) 块,从尾扫一遍统计结尾 \(0\) 块,再用 \(0\) 的数量减去这两部分即可,可能为负所以跟 \(0\) 取最大值。

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 239
#define X first
#define Y second


int main()
{
	ios::sync_with_stdio(false);
	
	int t;
	cin >> t;
	while(t--)
	{
		string s;
		cin >> s;
		
		int stzero = 0, endzero = 0;
		int zerocnt = 0;
		_for(i,0,s.size())
			if(s[i]=='0')
				zerocnt ++;
		
		_for(i,0,s.size())
			if(s[i]=='1')
				break;
			else
				stzero ++;
		
		_rep(i,s.size()-1,-1)
			if(s[i]=='1')
				break;
			else
				endzero ++;
		printf("%d\n",max(0,zerocnt-endzero-stzero));
	}
	return 0;
}

B. National Project

铺水泥,要求高质量水泥天数在 \(\left \lceil \frac{n}{2} \right \rceil\) 及以上,那其实我们可以假设要求 \(\left \lceil \frac{n}{2} \right \rceil\) 都是高质量水泥,然后和 \(n\) 取最大值即是答案。问题是要求 \(\left \lceil \frac{n}{2} \right \rceil\) 全是高质量水泥的天数怎么求?

我们可以将 \(g+b\) 这个值打包成一个 组 ,因为他们是连续的。如果要求全是高质量水泥,答案至少应该包含最大可能的 组 值,然后再来一些 \(g\) 来凑。说的有点抽象,具体一点就是比如现在要求 \(20\)天 高质量水泥,\(g==6,b==3\) ,则 组 就是 \(9\) ,你至少需要 \(3\) 组,再来 \(20-3×6=2\) 个零散的 \(g\) 来凑就能凑够 \(20\) 天高质量水泥,也就是 \(3×9+2=29\) 天。

特殊考虑 \(n|g\) 的情况即可。

PS:样例中一百万天坏天气震撼我心。

时间复杂度 \(O(1)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 239
#define X first
#define Y second

ll n, g, b, rnt;
int main()
{
	ios::sync_with_stdio(false);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> g >> b;
		ll tmpn = n/2 + (n&0x1);
		ll curhaveg = tmpn/g;
		rnt = curhaveg * (g+b);
		if(curhaveg*g == tmpn)
		{
			rnt -= g+b;
			curhaveg -= 1;
		}
		rnt += tmpn-curhaveg*g;
		printf("%lld\n",max(n,rnt));
	}
	return 0;
}

C. Perfect Keyboard

我是负责图论和数据结构的所以咳咳...脑回路清奇,我们将每个字母看成一个节点,他们相邻就连边呗,啥情况输出 \(NO\) ?首先我们排除一下重边,然后就是如果有环肯定不行,自行证明一下吧,还有一个就是一个点的度大于等于 \(3\) 不行,这个也自己想一下为什么。

我用的是并查集判环,度直接开数组 \(in\) 判断。最后图构建好以后找一个度为 \(1\) 的点 \(dfs\) 一下,没加到答案里的字母最后加进去就行了。为什么找一个度为 \(1\) 的点开始 \(dfs\) 一遍就好?因为这个无向图一定是一棵树,而且只能从儿子开始遍历,还是自己想一下为什么。

杀鸡用了牛刀...勿喷

时间复杂度主要用在判重边了,所以整体时间复杂度 为 \(O(nlogn+nα(n))=O(nlogn)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define X first
#define Y second

#define maxn 39
#define maxe 2003 
struct Djs
{
	int par[maxn];
	int high[maxn];
	void renew()
	{
		_for(i,1,maxn)
		{
			par[i] = i;
			high[i] = 0;
		}
	}
	int find(int x)
	{
		return par[x] == x ? x : par[x] = find(par[x]);
	}
	void unite(int x,int y)
	{
		x = find(x);
		y = find(y);
		if(x==y) return ;
		if(high[x]<high[y])
			par[x] = y;
		else
		{
			par[y] = x;
			if(high[x]==high[y])
				high[x] ++;
		}
	}
	bool same(int x,int y)
	{
		return find(x) == find(y);
	}
}djs;

struct G
{
    int n,m;
	int Next[maxe];
	int head[maxn];
	int ver[maxe];
	int tot;
	void add(int x,int y)
	{
		ver[++tot] = y,Next[tot] = head[x],head[x] = tot;
	}
	void renew()
	{
		tot = 0;
		n = 26;
		memset(head,0,sizeof(head));
	}
} g;

int to(char c)
{
	return c-'a'+1;
}

int vis[27];
int in[27];
string rnt;

void dfs(int x)
{
	rnt += x+'a'-1;
	vis[x] = 1;
	for(int i = g.head[x]; i; i = g.Next[i])
	{
		int y = g.ver[i];
		if(vis[y])
			continue;
		dfs(y);
	}
}

int main()
{
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	while(t--)
	{
		string s;
		cin >> s;
		memset(vis,0,sizeof(vis));
		memset(in,0,sizeof(in));
		rnt.clear();
		g.renew();
		djs.renew();
		
		set<pair<int,int>> st;
		int flag = 0;
		_for(i,0,s.size()-1)
		{
			if(st.count( {to(s[i]), to(s[i+1])} ) 
			|| st.count( {to(s[i+1]), to(s[i])} ) )
				continue;
			if(djs.same( to(s[i]), to(s[i+1]) ) )
			{
				flag = 1;
				break;
			}
			g.add(to(s[i]), to(s[i+1])),g.add(to(s[i+1]), to(s[i]));
			st.insert({to(s[i]), to(s[i+1])});
			djs.unite(to(s[i]), to(s[i+1]));
			in[to(s[i])] ++,in[to(s[i+1])] ++;
			if(in[to(s[i])] >= 3
			|| in[to(s[i+1])] >= 3)
			{
				flag = 1;
				break;
			}
		}
		
		if(flag)
		{
			printf("NO\n");
			continue;
		}
		
		int sta;
		_for(i,1,27)
			if(in[i]==1)
			 	sta = i;
		if(s.size()!=1)
			dfs(sta);
		_for(i,1,27)
			if(!vis[i])
				 rnt += i+'a'-1;
		printf("YES\n%s\n",rnt.c_str());
	}
	return 0;
}

D. Fill The Bag

既然盒子的大小就是二的倍数,那不妨把包的容量也拆成二进制。看看数据范围,盒子的大小最大也就那几十个,我们用一个数组 \(cnt\) 把他们装起来,$ cnt[5]=3$ 代表我们有 \(3\) 个 大小为 \(2^5=32\) 的盒子。

然后我们观察一下,拆是要代价的,但是合起来不要代价。那么对于盒子每一位为 \(1\) 的情况,如果有对应的盒子大小最好,如果没有那咱们考虑两种情况来凑一个:合或者拆。先合后拆,因为说过了合不要代价。所谓合就是把小盒子攒起来攒成大盒子。我们考虑一下,从小到大遍历每一位的时候,我们遍历过的那些位的小盒子肯定是没用的,所以不妨把他们都合起来,合到我们当前遍历到的位上。如果合完发现还是合不出来,那我们就只能从最近的更高位上,如果有盒子就一路拆下来。可以证明这样一定最优。假如我们现在遍历到 \(i=3\) ,我们发现 \(cnt[5]>0,cnt[6]>0\) 则我们一定是把 \(2^5=32\) 的盒子拆了而不是把 \(2^6=64\) 的盒子通过多拆一步得到,否则可能不是最优,证明过程略。

这样有拆有合,这个问题就算是完成了。时间复杂度 \(O(max(mlogk,k^2))\) ,其中 \(k\)\(n\) 在二进制表示下最大的位数,但其实 \(k\) 最大也就六十多啦,我们可以看成常数,这样的话时间复杂度就是 \(O(mlogk)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define X first
#define Y second
#define maxn 139

ll n, m;
ll biao[maxn];
ll cnt[maxn];
ll rnt;
void db()
{
	biao[0] = 1;
	_for(i,1,80)
		biao[i] = biao[i-1]*2;
}
void he(int x)
{
	cnt[x+1] += cnt[x]/2;
}

void chai(int x)
{
	int st = 0;
	_for(i,x,80)
		if(cnt[i])
		{
			st = i;break;
		}
	while(st!=x)
	{
		cnt[st] --;
		cnt[st-1]+=2;
		st --;
		rnt ++;
	}
}
int main()
{
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	db();
	while(t--)
	{
		cin >> n >> m;
		ll sum = 0;
		rnt = 0;
		memset(cnt,0,sizeof(cnt));
		_for(i,0,m)
		{
			ll tmp;
			cin >> tmp;
			sum += tmp;
			cnt[lower_bound(biao,biao+64,tmp)-biao] ++;
		}
		if(sum < n)
		{
			printf("-1\n");
			continue;
		}
		bitset<maxn> target = n;
		_for(i,0,80)
		{
			if(target[i])
			{
				if(cnt[i])
				{
					cnt[i]--;he(i);
				}
				else
					chai(i);
			}
			else
				he(i);
		}
		printf("%lld\n",rnt);
	}
	return 0;
}

后面几题实力不大够,告辞

posted @ 2020-02-13 11:42  Asurudo  阅读(410)  评论(0编辑  收藏  举报