Codeforces Round #368 (DIV 2)

昨晚的9点的CF,DIV2专场,为了rating change开了小号去。

说起来上一场也是DIV2专场开的小号去参加,然后第一个小号就上1900了,昨晚打完之后第二个小号也上DIV1了。蛮开心的。

最近这段时间在家里大概是参加不了12点半的CF了。毕竟补充睡眠。


部分题解:

Codeforces 707A Brain's Photos

题目链接:http://www.codeforces.com/problemset/problem/707/A

题意:给一个矩阵,判断矩阵里面有没有出现除了B,W,G之外的字母。

分析:水题,扫描一遍判断即可。

咳咳,刚开始比赛的时候刚打完DOTA人不清醒,题目没看完就去敲了,没看到G也算,只判断了B和W,结果竟然水过了测试数据。后来被人HACK之后重新看了一遍题意大骂自己傻X。

<span style="font-size:18px;">/***********************************************
 |Author: Fry
 |Created Time: 2016/8/20 20:59:40
 |File Name: A.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n,m;
	char c;
	bool f=false;
	while (~scanf("%d%d",&n,&m)){
		f=false;
		for (int i=1;i<=n;i++){
			for (int j=1;j<=m;j++){
				scanf(" %c",&c);
				if (c!='B'&&c!='W'&&c!='G') f=true;
			}
		}
		if (f) printf("#Color\n");
		else printf("#Black&White\n");
	}
	return 0;
}

</span>

Codeforces 707B Bakery

题目链接:http://www.codeforces.com/problemset/problem/707/B

题意:给一张图,有重边。其中某些点已经被标记,要求找到一个未被标记的点,使得其距离最近的标记过的点的距离最小。输出最小距离,若不存在这样的点输出-1。

分析:反证法可以得出,最终答案一定和一个标记的点相邻。

           那么只要枚举所有标记过的点,再枚举其所有的相邻的点即可。

           写的时候不是很清醒,代码挺丑。

<span style="font-size:18px;">/***********************************************
 |Author: Fry
 |Created Time: 2016/8/20 21:15:37
 |File Name: B.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct point
{
	int x,c;
	point (int _x=0,int _c=0){
		x=_x; c=_c;
	}
};
vector<point>v[N];
bool check[N];
int ans;
void DW(int x)
{
	for (int i=0;i<v[x].size();i++){
		if (!check[v[x][i].x]){
			if (ans==-1){
				ans=v[x][i].c;
			}
			else ans=min(v[x][i].c,ans);
		}
	}
}
int main()
{
	int n,m,k;
	int l,r,c;
	while (~scanf("%d%d%d",&n,&m,&k)){
		for (int i=1;i<=n;i++) v[i].clear();
		for (int i=1;i<=m;i++) {
			scanf("%d%d%d",&l,&r,&c);
			v[l].push_back(point(r,c));
			v[r].push_back(point(l,c));
		}
		memset(check,0,sizeof(check));
		for (int i=1;i<=k;i++){
			scanf("%d",&c);
			check[c]=1;
		}
		ans=-1;
		for (int i=1;i<=n;i++){
			if (check[i]){
				DW(i);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

</span>

Codeforces 707C Pythagorean Triples

题目链接:http://www.codeforces.com/problemset/problem/707/C

题意:已知直角三角形的一条边,求可能的另外两条边。三条边均为正整数,n<1e9,要求最终结果小于1e18。无解时输出-1

分析:很容易可以得知,当给出的边长度小于3时无解.

           那么接下来,我们假设给出的边为一条直角边。另外两条边长分别为a,b,其中b是斜边

           由勾股定理可以得出:a*a+n*n=b*b

           公式稍作调整:(b-a)(b+a)=n*n

           当n为奇数时,只需要使得b-a=1,b+a=n*n即可。

           当n为偶数时,只需要使得b-a=2,b+a=n*n/2即可。

           写完之后计算一下极限数据,偶数和奇数分别大概是2.5e17和5e17,不会超过上限。


<span style="font-size:18px;">/***********************************************
 |Author: Fry
 |Created Time: 2016/8/20 21:26:03
 |File Name: C.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;

int main()
{
	long long n,a,b;
	while (~scanf("%I64d",&n)){
		if (n<3){
			printf("-1\n");
			continue;
		}
		if (n%2){
			a=n*n/2;
			b=a+1;
			printf("%I64d %I64d\n",a,b);
		}
		else {
			a=n*n/2/2-1;
			b=a+2;
			printf("%I64d %I64d\n",a,b);
		}
	}
	return 0;
}

</span>

Codeforces 707D Persistent Bookcase

题目链接:http://www.codeforces.com/problemset/problem/707/D

题意:有一个有n行m列的书架,初始为空,现有q次操作,要求输出每次操作完之后书架上共有几本书。

           操作分为4种:

           1.在第i行第j列放入一本书。如果在操作之前第i行第j列已经有书了,那么忽略此次操作。

           2.将第i行第j列的书拿走。如果在操作之前第i行第j列没有书,那么忽略此次操作。

           3.将第i行的所有书反转。就是对于第i行的所有列,如果这个位置有书,那么将它拿走,如果这个位置没有书,那么放一本书到这个位置。

           4.将现在的整个书架的状态返回到第k次操作完之后的样子。

           1<=n,m<=1e3,1<=q<=1e5


分析:其实原题面有提到过持久化数据结构,听队友说她这题直接写了持久树就A了。

           我没想到持久树(好吧其实是不会写),用了点方法过去了。

           将所有的操作建成为一棵树,然后在树上操作就简单了。

           首先根节点一定是第0次操作,也就是初始值为空,对于之后的第i个操作,如果这个操作不是操作4,那么它的父亲节点是第i-1个操作,如果这个操作是操作4并且是回到第k次操作完之后的样子,那么这个节点的父亲是第k次操作。

           建完树之后进行一次树的遍历。回溯的时候要把原有的操作删除。

           需要注意的是原有的操作可能并不存在,比如原本第i行第j列已经有书了,这次操作又要求在这个位置添加一本书,那么就不会进行操作,同样回溯的时候也不能将这本书拿走。

          

           关于内存,队友用持久化能直接过的话,实际上n*q的内存是毫无问题的。我的方法的内存是O(min(q,n*m))。

           时间复杂度,暴力操作的话,添加和删除操作的复杂度是O(1),反转的复杂度是O(m),虽然感觉O(q*m)也能过的样子,

           不过我还是换了个姿势,记录的时候用了一个标记来记录每一行的反转情况,并且每一行开一个set来记录有哪些位置有书,这样最终时间复杂度,添加和删除操作是O(logm),反转的复杂度是O(1),最终复杂度O(q*logm)


<span style="font-size:18px;">/***********************************************
 |Author: Fry
 |Created Time: 2016/8/20 21:37:29
 |File Name: D.cpp
 |Copyright: 
 |  For personal use, feel free to use
 |  Otherwise call me at http://blog.csdn.net/fry_guest 
***********************************************/
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int ans[N],t[N],x[N],y[N];
set<int>s[N];
vector<int>v[N];
bool check[N];
int n,m;
void DW(int a,int sum)
{
	bool f=false;
	if (t[a]!=4){
		if (t[a]==3) {
			f=true;
			if (!check[x[a]]) sum-=s[x[a]].size();
			else sum-=m-s[x[a]].size();
			check[x[a]]=!check[x[a]];
			if (!check[x[a]]) sum+=s[x[a]].size();
			else sum+=m-s[x[a]].size();
		}
		else if (t[a]==2) {
			if (!check[x[a]]){
				if (s[x[a]].find(y[a])!=s[x[a]].end()){
					s[x[a]].erase(y[a]);
					sum--;
					f=true;
				}
			}
			else {
				if (s[x[a]].find(y[a])==s[x[a]].end()){
					s[x[a]].insert(y[a]);
					sum--;
					f=true;
				}
			}
		}
		else if (t[a]==1) {
			if (!check[x[a]]){
				if (s[x[a]].find(y[a])==s[x[a]].end()){
					s[x[a]].insert(y[a]);
					sum++;
					f=true;
				}
			}
			else {
				if (s[x[a]].find(y[a])!=s[x[a]].end()){
					s[x[a]].erase(y[a]);
					sum++;
					f=true;
				}
	
			}
		}
	}
	ans[a]=sum;
	for (int i=0;i<v[a].size();i++){
		DW(v[a][i],sum);
	}
	if (f){
		if (t[a]==3)check[x[a]]=!check[x[a]];
		if (t[a]==2){
			if (!check[x[a]]) s[x[a]].insert(y[a]);
			else s[x[a]].erase(y[a]);
		}
		if (t[a]==1){
			if (!check[x[a]]) s[x[a]].erase(y[a]);
			else s[x[a]].insert(y[a]);
		}
	}
}
int main()
{
	int q;
	while (~scanf("%d%d%d",&n,&m,&q)){
		memset(check,0,sizeof(check));
		for (int i=0;i<=n;i++) v[i].clear();
		for (int i=0;i<=n;i++) s[i].clear();
		for (int i=1;i<=q;i++){
			scanf("%d",&t[i]);
			if (t[i]<3){
				scanf("%d%d",&x[i],&y[i]);
			}
			else scanf("%d",&x[i]);
		}
		for (int i=1;i<=q;i++){
			if (t[i]==4){
				v[x[i]].push_back(i);
			}
			if (i!=q&&t[i+1]!=4){
				v[i].push_back(i+1);
			}
		}
		if (t[1]!=4){
			v[0].push_back(1);
		}
		for (int i=0;i<v[0].size();i++){
			DW(v[0][i],0);
		}
		for (int i=1;i<=q;i++){
			printf("%d\n",ans[i]);
		}

	}
	return 0;
}

</span>

          

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值