2020级训练赛10.30

等比数列三角形

一、题目

点此看题

二、解法

看到这道题,第一个想法就是暴枚 ,我们先考虑公比 k k k的范围。(规定边长递增)
据题, a + a k > a k 2 a+ak>ak^2 a+ak>ak2
⇒ 1 + k > k 2 \Rightarrow 1+k>k^2 1+k>k2
⇒ 5 4 > ( k − 1 2 ) 2 \Rightarrow \frac{5}{4}>(k-\frac{1}{2})^2 45>(k21)2
⇒ 1 ≤ k < 5 + 1 2 \Rightarrow 1\leq k<\frac{\sqrt{5}+1}{2} 1k<25 +1
我们设 k = q / p k=q/p k=q/p p , q p,q p,q互质),所以首项必须要是 p 2 p^2 p2的倍数,可以得到 p ≤ n p\leq \sqrt{n} pn 。我们枚举 q q q,可以得到所对应的 p p p的个数,然后考虑末项(因为要保证末项 ≤ n \leq n n),时间复杂度 O ( n log ⁡ n ) O(\sqrt{n}\log n) O(n logn)

#include <cstdio>
#include <cmath>
#define int long long
const int MAXN = 1e6+5;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,gg,ok[MAXN],p[MAXN],ans;
void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		gg=i*(2/(sqrt(5)+1));//算分子的最小值(开区间) 
		ok[i]=i-gg;//减去得到合法取值 
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i<<1;j<=n;j+=i)
			ok[j]-=ok[i];//要保证互质,用类似埃氏筛的方法 
	}
}
signed main()
{
	init(1e6);
	n=read();
	for(int x=1;x*x<=n;x++)
		ans+=(n/(x*x))*ok[x];//枚举末项,末项的取值乘上公比的取值(末项是q^2*一个数) 
	printf("%lld\n",ans);
}

一个简单的询问

一、题目

点此看题

二、解法

如题所示,本题难度 NOI/NOI+/CTSC \text{NOI/NOI+/CTSC} NOI/NOI+/CTSC
看到区间问题,很有用的一个方向是差分,我们尝试对原问题进行一些变形:
x [ l 1 , r 1 ] × x [ l 2 , r 2 ] = ( x [ 1 , r 1 ] − x [ 1 , l 1 − 1 ] ) × ( x [ 1 , r 2 ] − x [ 1 , l 2 − 1 ] ) = x[l_1,r_1]\times x[l_2,r_2]=(x[1,r_1]-x[1,l_1-1])\times (x[1,r_2]-x[1,l_2-1])= x[l1,r1]×x[l2,r2]=(x[1,r1]x[1,l11])×(x[1,r2]x[1,l21])=
x [ 1 , r 1 ] × x [ 1 , r 2 ] + x [ 1 , l 1 − 1 ] × x [ 1 , l 2 − 1 ] − x [ 1 , l 2 − 1 ] × x [ 1 , r 1 ] − x [ 1 , l 1 − 1 ] × x [ 1 , r 2 ] x[1,r_1]\times x[1,r_2]+x[1,l_1-1]\times x[1,l_2-1]-x[1,l_2-1]\times x[1,r_1]- x[1,l_1-1]\times x[1,r_2] x[1,r1]×x[1,r2]+x[1,l11]×x[1,l21]x[1,l21]×x[1,r1]x[1,l11]×x[1,r2]
发现它变成了四个独立的子问题,而这四个子问题都只有两个变量,莫队很容易写出来,就是维护一个和,时间复杂度 O ( n n ) O(n\sqrt{n}) O(nn )

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 50005; 
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,k,q,l,r,a[MAXN],T[MAXN][2];
long long sum,ans[MAXN];
struct node
{
	int l,r,id,ok;
	bool operator < (const node &R) const
	{
		return l/m==R.l/m?r<R.r:l<R.l;
	}
}s[MAXN*4];
void remove(int x,bool t)
{
	T[a[x]][t]--;sum-=T[a[x]][t^1];
}
void add(int x,bool t)
{
	T[a[x]][t]++;sum+=T[a[x]][t^1];
}
int main()
{
	n=read();m=sqrt(n);
	for(int i=1;i<=n;i++)
		a[i]=read();
	q=read();
	for(int i=1;i<=q;i++)
	{
		int l1=read(),r1=read(),l2=read(),r2=read();
		s[++k]=node{r1,r2,i,1};
		s[++k]=node{l1-1,l2-1,i,1};
		s[++k]=node{l1-1,r2,i,-1};
		s[++k]=node{r1,l2-1,i,-1};
	}
	for(int i=1;i<=k;i++)
	{
		if(s[i].l<=s[i].r) continue;
		swap(s[i].l,s[i].r);
	}
	sort(s+1,s+1+k);
	for(int i=1;i<=k;i++)
	{
		int L=s[i].l,R=s[i].r;
		while(l<L) remove(++l,0);
		while(l>L) add(l--,0);
		while(r>R) remove(r--,1);
		while(r<R) add(++r,1);
		ans[s[i].id]+=s[i].ok*sum;
	}
	for(int i=1;i<=q;i++)
		printf("%lld\n",-ans[i]);
}

愤怒的小鸟

一、题目

点此看题

二、解法

很容易想到装压,装压实现也很简单,不展开。
就讲一个点,考虑枚举两个猪的时候,只需要话费 O ( n ) O(n) O(n),因为第一只猪是固定的,因为本题每只猪都要被打到,而且猪被打的顺序不重要,所以第一只猪就是状态中没有没打到的猪。

#include <cstdio>
#include <cstring>
#include <iostream>
#define eps 1e-6
using namespace std;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int T,n,m,dp[1<<18];
double x[20],y[20];
bool chk(double x)
{
	return x<eps && x>-eps;
}
int main()
{
	T=read();
	while(T--)
	{
		n=read();m=read();
		m=1<<n;
		for(int i=1;i<=n;i++)
			scanf("%lf %lf",&x[i],&y[i]);
		memset(dp,0x3f,sizeof dp);
		dp[0]=0;
		for(int i=0;i<m-1;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(!(i&(1<<j-1)))
					dp[i|(1<<j-1)]=min(dp[i|(1<<j-1)],dp[i]+1);
			}
			int j=1;
			for(;j<=n;j++)
				if(!(i&(1<<j-1)))
					break;
			for(int k=1;k<=n;k++)
			{
				if(i&(1<<k-1) || x[j]==x[k]) continue;
				double a=(y[j]*x[k]-y[k]*x[j])/(x[k]*x[j]*x[j]-x[j]*x[k]*x[k]);
				double b=(y[j]-a*x[j]*x[j])/x[j];
				if(a>=0) continue;
				int to=i;
				for(int l=1;l<=n;l++)
					if(chk(a*x[l]*x[l]+b*x[l]-y[l]))
						to|=(1<<l-1);
				dp[to]=min(dp[to],dp[i]+1);
			}
		}
		printf("%d\n",dp[m-1]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值