2017.12.09【NOIP提高组】模拟赛B组总结

这次有点萎,本来第三题可以95分的,结果数组开小了,只得了65分。
60+60+65=185。
第一题奇奇怪怪地我竟然没有想到方法。
T1: 【NOIP2013模拟联考11】剑与魔法(dragons)
首先我们记录下所有点之前最多可以取的数量。
然后就用一个堆(小根堆),每次如果放的进去就放,放不进去就直接看看可不可以替换堆顶。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n;
int a[200010];
int x[200010];
int kind[200010];
int d[200010];
void up(int x)
{
	while((x>>1)>0&&d[x]<d[x>>1])
	{
		swap(d[x],d[x>>1]);
		x=x>>1;
	}
}
void down(int x)
{
	int k;
	while(((x<<1)<=d[0]&&d[x]>d[x<<1])||((x<<1)+1<=d[0]&&d[x]>d[(x<<1)+1]))
	{
		k=x<<1;
		if(k+1<=d[0]&&d[k+1]<d[k])++k;
		swap(d[x],d[k]);
		x=k;
	}
}
int main()
{
	scanf("%d\n",&n);
	int i;
	char k;
	for (i=1;i<=n;++i)
	{
		scanf("%c%d\n",&k,&x[i]);
		if(k=='c') kind[i]=0;
		if(k=='e') kind[i]=1;
	}
	int ans=2147483647;
	for (i=n-1;i>=1;--i)
		if(kind[i]==0) a[i]=ans;
		else ans=min(ans,x[i]-1);
	for (i=1;i<=n;++i)
		if(kind[i]==0)
		{
			if(d[0]<a[i])
			{
				d[++d[0]]=x[i];
				up(d[0]);
			}
			else
			{
				if(d[1]<x[i])
				{
					d[1]=x[i];
					down(1);
				}
			}
		}
	long long sum=0;
	for (i=1;i<=d[0];++i) sum+=d[i];
	printf("%lld\n",sum);
}

T2: 【NOIP2013模拟联考11】矩形(rect)
30%直接暴力4重。
60%把每次枚举的两条线变成一条,然后结束后用组合数计算。3重。
100%把每次枚举的那一条线预处理出来,然后判断我们枚举的两条线的交集。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,ans,sum;
int par[2010],ver[2010];
int par2[2010][2010];
bool bz[2010][2010];
int main()
{
	scanf("%d",&n);
	int i,j,k,l,x1[2010],y1[2010],x2[2010],y2[2010];
	for (i=1;i<=n;++i)
	{
		scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
		if(x1[i]==x2[i]) par[++par[0]]=i;
		else ver[++ver[0]]=i;
	}
	memset(bz,0,sizeof(bz));
	for (i=1;i<=par[0];++i)
		for (j=1;j<=ver[0];++j)
				if(min(x1[ver[j]],x2[ver[j]])<=x1[par[i]]&&max(x1[ver[j]],x2[ver[j]])>=x1[par[i]]&&min(y1[par[i]],y2[par[i]])<=y1[ver[j]]&&max(y1[par[i]],y2[par[i]])>=y1[ver[j]])	
				{
					par2[i][++par2[i][0]]=j;	
					bz[i][j]=true;			
				}
	for (i=1;i<=par[0]-1;++i)
		for (j=i+1;j<=par[0];++j)
		{
			sum=0;
			for (k=1;k<=par2[i][0];++k)
				if(bz[j][par2[i][k]])++sum;
			ans=ans+(sum*(sum-1))/2;
		}
	printf("%d\n",ans);
}

T3:【NOIP2013模拟11.4A组】善良的精灵(fairy)
我们可以把这题转化成两个方法:
①二分图染色(暴力)时间复杂度 O ( m 2 ) O(m^2) O(m2)
②差分约束,时间复杂度 O ( m ) O(m) O(m)
在这里就只讲差分约束了。
首先我们把这个图看成一个树,然后有环的时候就把它连成一条反阻边,然后按照差分约束的原理,我们就可以在当前的地方打一个+1表示,然后在反阻边另一端的点上打一个-1,然后在从底向头找,就好了。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int next[20010],last[20010],tov[20010],num[20010];
int n,m,tot;
bool flag;
int f1[20010],f2[20010],g1[20010],g2[20010];
bool bz[20010],bz1[20010];
int deep[10010];
int ans[10010];
void insert(int x,int y,int i)
{
	tov[++tot]=y;
	num[tot]=i;
	next[tot]=last[x];
	last[x]=tot;
}
void dfs(int t,int fa)
{
	int i=last[t];
	int y;
	deep[t]=deep[fa]+1;
	while(i)
	{
		y=tov[i];
		if(y==fa) 
		{
			i=next[i];
			continue;
		}
		if(deep[y]==0)
		{
			bz[i]=1;
			dfs(y,t);
		}
		else
		if(deep[y]<deep[t])
		{
			if((deep[t]-deep[y]+1)%2)
			{
				++tot;
				g1[t]+=1;
				g1[y]-=1;
				f1[num[i]]=1;	
			}
			else
			{
				g2[t]+=1;
				g2[y]-=1;
				f2[num[i]]=1;
			}
			i=next[i];
			continue;
		}
		i=next[i];
	}
}
void find(int x)
{
    bz1[x]=true;
    int i=last[x];
    while (i!=0)
    {
        if (bz[i])
        {
            find(tov[i]);
            f1[num[i]]=g1[tov[i]];
            f2[num[i]]=g2[tov[i]];
            g1[x]=g1[x]+g1[tov[i]];
            g2[x]=g2[x]+g2[tov[i]];
        }
        i=next[i];
    }
}
int main()
{
	freopen("fairy.in","r",stdin);
	freopen("fairy.out","w",stdout);
	scanf("%d%d",&n,&m);
	int i,j,x,y;
	for (i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		insert(x,y,i);
		insert(y,x,i);
	}
	tot=0;
	deep[1]=1;
	memset(deep,0,sizeof(deep));			
	for(i=1;i<=n;i++)
		if(!deep[i]) dfs(i,0);	
	if(tot==0)
	{
		printf("%d\n",m);
		for (i=1;i<=m;++i)
			printf("%d ",i);
	}
	else
	{
		for (i=1;i<=n;++i)
			if(!bz1[i]) find(i);
		for (i=1;i<=m;++i)
			if(f1[i]==tot&&f2[i]==0)
				ans[++ans[0]]=i;
		printf("%d\n",ans[0]);
		for (i=1;i<=ans[0];++i)
			printf("%d ",ans[i]);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值