清北5

1.三角形。
方法一:组合数:
先假设所有的直线都不平行,则答案为C(n,3)。
当然肯定有平行的直线,我们把所有平行的直线看做一组,将每组平行线的数量统计出来,
如果有一组数量num[i]==2,那么这两个平行线和剩下的所有直线都不能组成三角形,
从总答案中减去C(n-2,1);
若num[i]>2,则除了前面一种情况不合法之外,从这里面选出来的三条也是不合法的。即减去C(num[i],3);
方法二:类似DP的思想:
设数组dp[i][j],表示从前i组直线中选出j条直线的方案数,因为三角形只有3条边,则j只有3个值,1,2,3.在和方法一一样的预处理之后,O(n)dp就可以了。
转移方程:dp[i][3]=dp[i-1][3]+b[i]*dp[i-1][2];
dp[i][2]=dp[i-1][2]+b[i]*dp[i-1][1];
dp[i][1]=dp[i-1][1]+b[i];(根据dp的涵义推出来的。方法和清北1的幸运数有异曲同工之妙。)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=310000;
int read()
{
	int res=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		res=res*10+ch-'0';
		ch=getchar();
	}
	return res*f;
}
int gcd(int a,int b)
{
	if(!b) return a;
	else return gcd(b,a%b);
}
struct node
{
	int a;
	int b;
}l[maxn];
int cmp(node a,node b)
{
	if(a.a<b.a) return 1;
	if(a.a==b.a&&a.b<b.b) return 1;
	return 0;
}
int b[maxn],n,num;
ll dp[maxn][4];
int main()
{
	freopen("trokuti.in","r",stdin);
	freopen("trokuti.out","w",stdout);
	n=read();
	for(int x,y,z,g,i=1;i<=n;i++)
	{
		x=read();y=read();z=read();
		if(x==0||y==0) { l[i].a=x;l[i].b=y;}
		else { 
		    g=gcd(x,y);
		    l[i].a=x/g;l[i].b=y/g;}
	}
	sort(l+1,l+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		num++;b[num]=1;
		while(l[i].a==l[i+1].a&&l[i].b==l[i+1].b&&i<n)
		{
			b[num]++;
			i++;
		}
	}
	dp[1][3]=dp[2][3]=dp[1][2]=0;
	dp[1][1]=b[1];dp[2][1]=dp[1][1]+b[2];
	dp[2][2]=dp[1][2]+b[2]*dp[1][1];
	for(int i=3;i<=num;i++)
	{
		dp[i][1]=dp[i-1][1]+b[i];
		dp[i][2]=dp[i-1][2]+b[i]*dp[i-1][1];
		dp[i][3]=dp[i-1][3]+b[i]*dp[i-1][2];
	}
	cout<<dp[num][3];
	fclose(stdin);
	fclose(stdout);
	return 0;
}

2.列车调度:
暴力模拟一下,发现和单调栈相似,和清北2的T2有相同之处。

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=1e5+10;
int read()
{
	int res=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		res=res*10+ch-'0';
		ch=getchar();
	}
	return res*f;
}
int t,n,a[maxn];
int f[maxn];
int main()
{
	freopen("manage.in","r",stdin);
	freopen("manage.out","w",stdout);
n=read();
	for(int i=1;i<=n;i++)
	 a[i]=read();
	t++;f[t]=a[1];
	for(int i=2;i<=n;i++)
	{
		if(a[i]>f[t])
		{
			t++;f[t]=a[i];
		}
		else
		{
			int l=1,r=t,ans=l;
			while(l<=r)
			{
				int mid=(l+r)/2;
				if(f[mid]>a[i])
				{
					ans=mid;r=mid-1;
				}
				else l=mid+1;
			}
			f[ans]=a[i];
		}
	}
	cout<<t;
	fclose(stdin);
	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值