noip57

发布时间:2022-07-05 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了noip57脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

题并不难,然而在一些细节和心态上出锅...

T1

考场想法:按题意写即可....

没啥好说的...

按题目说的判断不合法即可。

注意一些细节即可...

比如大小写和输出格式要求。

然后,自然数不包括负数...

T2

考场想法:想不出来暴力,那干脆写个假贪心吧,每回从前往后扫一遍串,碰见个 (AP)(PP) 就删,码完后,觉得假斩了,但没管了就,直接下一道题了...

竟然A了

没啥好说的...

说一下b哥午饭时说的正解:

(A) 每回和 (P) 匹配显然最优,那就成括号匹配问题了,最后剩下的 (P) 两两消了即可。

T3

考场想法:看了天题面,一直搞不懂菱形如何判,干脆写了个40pts的暴力,写完后继续看,发现其实是让某两个 (P) 从某一个共同的类派生出来的,并且这俩 (P) 有一个共同的派生出来的类。然后我就去想了并查集,发现不好搞。写暴力的话,觉得自己应该会调很长时间,此时还有大概1h,T4还没动。

然而一堆切的,没有想到 (bITset) ,还是菜了....

40pts:只判断前两个条件。

60pts:暴力去判断第三个条件。

100pts:

读题发现如果四个类构成菱形需要满足两个条件:

  1. x,y需要满足至少是由一个共同的类派生过来的。
  2. x,y之间没有派生关系。

所以对每个类开个 (bitset) 设为 (vis)(vis_{i}) 编号后第 (i) 个类是由那些类派生过来的。

每回新声明一个类,就去枚举输入的派生类对,先判断之前是否声明过了,然后去看是否其中有一对满足菱形的条件,则这个声明就是不合法的。

如果没有构成菱形,还需判断当前要声明的类之前是否声明过,都合法的话就给这个类编上号,并更新其 (vis) 即可。

这些都可以用 (bitset+map) 实现。

Code
#include<;map>
#include<cstdio>
#include<cctyPE>
#include<bitset>
#include<string>
#include<iostream>
#define re register
using std::cin;
using std::cout;
using std::map;
using std::bitset;
using std::string;
#define MAX 1003
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}oma;
	auto line = []() { PRintf("n"); };
	auto begin = []() { printf("beginn"); }; auto end = []() { printf("endn"); };
}using namespace some;
namespace OMA
{
	int n,cnt,tot;
	string k,tmp[MAX];
	map<string,int>id;
	map<string,bitset<MAX> >vis;
	auto main = []() -> signed
	{
		freopen("class.in","r",stdin); freopen("class.out","w",stdout);
		oma >> n;
		while(n--)
		{
			bool judge = true;
			cin >> k >> tmp[cnt = 0];
			while(tmp[cnt][0]!=';')
			{
				cin >> tmp[++cnt];
				if(tmp[cnt][0]==';')
				{ break ; }
			}
			//line(),begin(); printf("how many strings: %dn",cnt);
			for(re int i=1; i<cnt; i++)
			{
				if(!id[tmp[i]])
				{ judge = false; break ; }
				for(re int j=i+1; j<cnt; j++)
				{
					if(vis[tmp[i]][id[tmp[j]]]||vis[tmp[j]][id[tmp[i]]])
					{ continue ; }
					if((vis[tmp[i]]&vis[tmp[j]]).count())
					{ judge = false; break ; }
				}
				if(!judge)
				{ break ; }
			}
			if(!id[k]&&judge)
			{
				id[k] = ++tot;
				//cout << k << 'n';
				for(re int i=1; i<cnt; i++)
				{
					vis[k][id[tmp[i]]] = 1;
					vis[k] |= vis[tmp[i]];
				}
				printf("okn");
			}
			else
			{ printf("greskan"); }
			//end();
		}
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T4

考场想法:读完题就想个 (n^{2}2^{n}) 的zz做法,写到一半才发现连最低的档都过不去,于是再去读了遍题,发现自己 (k-degree) 子图的定义刚刚弄错了,我竟然nt到不去看图的解释。但仍然不知道该怎么写,然后突发奇想,求联通块,计算每个联通块的答案取最优即可想到这个是因为自己还是没搞懂(kd)子图,码完调完就已经11:50,还有十分钟结束,突然发现自己又又写假了,直接按图上画的,先找 (1-d) ,再删去度数为1的,依次推下去就行了,但已经时间再写了,于是检查了一下代码,发现没什么问题,比赛就结束了。

这假做法竟然有35pts...

有负数,少拿5pts,可恶

40pts:求遍联通块,求答案取最优,注意常数项可能为负数,所以答案要设成 (-iNF)

100pts:

就是考场上最后的想法...

先求出度数最大为多少,然后去枚举 (k-degree) 子图的度数。

由于从 (k-1)(k) 过程中,删点的操作可能会让图变得不联通(可以参考题面中的图),所以要对于每一个对应 (k-d) 联通块都求一遍答案。

(k) 更新到 (k+1) 时,需要判断点的度数是否合法,包括两个方面,原先 (k) 不合法的,更新后肯定还是不合法,删掉一些 (k) 中合法的点后,可能会导致其他点的度数也不合法。删点操作可以用 (topo) 来实现。

注意一下度数为0的点,一开始就设置为不合法即可,还有上边说的负数问题。

Code
#include<queue>
#include<cstdio>
#include<cctype>
#define re register
#define MAX 1000003
#define int64_t long long
const int64_t INF = -1e18;
using std::queue;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	struct graph
	{
		int next;
		int to;
	}Edge[MAX<<1];
	int cnt=1,head[MAX];
	auto add = [](int u,int v) -> void { edge[++cnt] = (graph){head[u],v},head[u] = cnt; };
	auto max = [](int a,int b) -> int { return a>b?a:b; };
}using namespace some;
namespace OMA
{
	bool vis[MAX],ban[MAX];
	int n,m,M,N,B,xam,du[MAX];
	int64_t ns,ms,bs,s=INF,k,sou;
	void DFs(int u)
	{
		if(vis[u])
		{ return ; }
		vis[u] = true; ns++;
		for(re int i=head[u],v; i; i=edge[i].next)
		{
			v = edge[i].to;
			ms += !ban[v],bs += ban[v];
			if(!vis[v]&&!ban[v])
			{ dfs(v); }
		}
	}
	auto topo = [](int lim) -> void
	{
		queue<int>q;
		for(re int i=1; i<=n; i++)
		{ if(du[i]==lim){ ban[i] = true; q.push(i); } }
		while(!q.empty())
		{
			int u = q.front(); q.pop();
			for(re int i=head[u],v; i; i=edge[i].next)
			{
				v = edge[i].to;
				if(!ban[v])
				{
					if((--du[v])==lim)
					{ ban[v] = true; q.push(v); }
				}
			}
		}
	};
	auto main = []() -> signed
	{
		freopen("kdgraph.in","r",stdin); freopen("kdgraph.out","w",stdout);
		cin >> n >> m >> M >> N >> B;
		for(re int i=1,u,v; i<=m; i++)
		{ cin >> u >> v; add(u,v),add(v,u); du[u]++,du[v]++; }
		for(re int i=1; i<=n; i++)
		{ if(!du[i]){ ban[i] = true; } xam = max(xam,du[i]); }
		cnt = 0;
		for(re int i=1; i<=xam; i++)
		{
			for(re int j=1; j<=n; j++)
			{ vis[j] = false; }
			for(re int j=1; j<=n; j++)
			{
				if(!vis[j]&&!ban[j])
				{
					ms = ns = bs = 0; dfs(j);
					ms /= 2,sou = M*ms-N*ns+B*bs;
					//printf("ns=%lld ms=%lld bs=%lld sou=%lldn",ns,ms,bs,sou);
					if(s<sou)
					{ s = sou,k = i; }
					else if(s==sou)
					{ k = max(k,i); }
				}
			}
			topo(i);
		}
		printf("%lld %lldn",k,s);
		return 0;
	};
}
signed main()
{ return OMA::main(); }

最后两道题有些着急了,主要是因为题面一直没看明白,导致照着错误的理解来写,再看一遍题面,就把原先代码删了再写,很浪费时间,再加上自己觉得时间不够了,导致后两道题出问题,一些细节都没有注意到。

好吧,对于这套题来说,时间是够的,还是自己心态问题,类似的情况应当避免发生。

脚本宝典总结

以上是脚本宝典为你收集整理的noip57全部内容,希望文章能够帮你解决noip57所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。