NOIP2018 集训(二)

A题 神炎皇

问题描述

神炎皇乌利亚很喜欢数对,他想找到神奇的数对。

对于一个整数对 \((a,b)\) ,若满足 \(a+b\leq n\)\(a+b\)\(ab\) 的因子,则称
为神奇的数对。请问这样的数对共有多少呢?

输入格式

一行一个整数 \(n\)

输出格式

一行一个整数表示答案,保证不超过 \(64\) 位整数范围。

数据范围与约定

对于 \(20\%\) 的数据 \(n\leq 1000\) ;

对于 \(40\%\) 的数据 \(n\leq 10^5\) ;

对于 \(60\%\) 的数据 \(n\leq 10^7\) ;

对于 \(80\%\) 的数据 \(n<=10^{12}\) ;

对于 \(100\%\) 的数据 \(n<=10^{14}\)

样例

样例输入
21
样例输出
4
23

题解

首先暴力或者打表,都只能得20分

//打表程序
#include<bits/stdc++.h>
using namespace std;
inline char get(){
	static char buf[30],*p1=buf,*p2=buf;
	return p1==p2 && (p2=(p1=buf)+fread(buf,1,30,stdin),p1==p2)?EOF:*p1++;
}
inline long long read(){
	register char c=get();register long long f=1,_=0;
	while(c>'9' || c<'0')f=(c=='-')?-1:1,c=get();
	while(c<='9' && c>='0')_=(_<<3)+(_<<1)+(c^48),c=get();
	return _*f;
}
int main(){
	freopen("watch.txt","w",stdout);
	long long a,b;
	long long n=0;
	cout<<"a[]={0";
	while(n<=10000005){
		long long now=0;
		n++;
		for(register long long i=1;i<=n;i++){
			for(register long long j=1;j<=n-i;j++){
				//cout<<n<<":"<<i<<" "<<j<<endl;
				if(i+j<=n && (i*j)%(i+j)==0){
					//cout<<n<<":"<<i<<" "<<j<<endl;
					now++;
				}
			}
		}
		cout<<","<<now;
	}
	cout<<"};";
	return 0;
}

事实上,
这个时候让我们来看一下数据范围,对于100%的数据$ n\le10^{14} \(,为什么是\) 10 ^{14} $而不是\(10^{18}\)呢?说明这个题要用一个复杂度为$ O(\sqrt{n}) \(的算法(程序1s通常可以运算\) 10^{7} \(次),于是乎我们再次对式子进行变形,尽量向\)O(\sqrt{n})\(靠拢 先假设现在有满足条件的数\)a,b\(,同时我们设\)d=gcd(a,b)\(,则此时一定有:\)\(a^{'}=\frac{a}{k} , b^{'}=\frac{b}{k}\)$
对式子进行变形,则可以得到:

\[k(a′+b′)\%k^2a′b′=0 \]

\(∵k(a′+b′)\le n\)
$ ∴a'+b’ \le \sqrt{n}$

代码如下:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N=10000005;

LL n,Ans;
int m,f[N],p[N],phi[N];

int main()
{
#ifndef ONLINE_JUDGE
	freopen("uria.in","r",stdin);
	freopen("uria.out","w",stdout);
#endif
	cin>>n;
	int lim=int(sqrt(n)); 
	for(int i=2;i<=lim;i++)
	{
		if(!f[i])
			p[++m]=i,phi[i]=i-1;
		for(int j=1;j<=m&&i*p[j]<=lim;j++)
		{
			f[i*p[j]]=1;
			if(i%p[j]==0)
			{
				phi[i*p[j]]=phi[i]*p[j];break;
			}
			phi[i*p[j]]=phi[i]*(p[j]-1);
		}
		Ans+=n/i/i*phi[i];
	}
	cout<<Ans;
	return 0;
}

##B题 降雷神 #### 问题描述 降雷皇哈蒙很喜欢雷电,他想找到神奇的电光。

哈蒙有 \(n\) 条导线排成一排,每条导线有一个电阻值,神奇的电光
只能从一根导线传到电阻比它大的上面,而且必须从左边向右传导,
当然导线不必是连续的。

哈蒙想知道电光最多能通过多少条导线,还想知道这样的方案有
多少。

输入格式

第一行两个整数 \(n\)\(t\)\(t\) 表示数据类型

第二行 \(n\) 个整数表示电阻。

输出格式

第一行一个整数表示电光最多能通过多少条导线。

如果 \(t=1\) 则需要输出第二行,表示方案数,对 \(123456789\) 取模。

数据范围与约定

对于 \(20\%\) 的数据 \(n\leq 10\)

对于 \(40\%\) 的数据 \(n\leq 1000\)

对于另外 \(20\%\) 的数据 \(t=0\)

对于另外 \(20\%\) 的数据保证最多能通过不超过 \(100\) 条导线;

对于 \(100\%\) 的数据 \(n\leq 100000\) ,电阻值不超过 \(100000\)

样例

样例输入
5 1
1 3 2 5 4
样例输出
3
4

题解

对于\(t=0\)的时候,直接求出最长上升子序列即可(二十分做法)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000005;
int n, a[maxn], f[maxn], g[maxn], ans;
int t;
int main() {
    cin >> n >> t;
    for (int i = 1; i <= n; i++) cin >> a[i];
	memset(g,0x3f,sizeof(g));
    memset(f,0,sizeof(f));
    g[0]=0;
    ans=0;
    for(int i=1;i<=n;i++){
        f[i]=lower_bound(g+1,g+ans+1,a[i])-g;
        g[f[i]]=a[i];
        ans=max(ans,f[i]);
    }
    cout<<ans<<endl;
    return 0;
}

事实上,我们需要做的只是在求最长上升子序列的同时再求出方案数即可。

#include<bits/stdc++.h>
using namespace std;

const int N=100005,M=5000005,Mod=123456789;

int lc[M],rc[M],s[M],rt[N],f[N],g[N],Max[N],Ans1,Ans2,tot,n,type;

void Add(int& x,int l,int r,int p,int k)
{
	if(!x)
		x=++tot,lc[x]=rc[x]=s[x]=0;
	int Mid=l+r>>1;
	s[x]=(s[x]+k)%Mod;
	if(l==r)
		return;
	if(p<=Mid)
		Add(lc[x],l,Mid,p,k);
	else
		Add(rc[x],Mid+1,r,p,k);
}

int Ask(int x,int l,int r,int p)
{
	if(r<=p)
		return s[x];
	int Mid=l+r>>1;
	if(p<=Mid)
		return Ask(lc[x],l,Mid,p);
	return (s[lc[x]]+Ask(rc[x],Mid+1,r,p))%Mod;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("hamon.in","r",stdin);
	freopen("hamon.out","w",stdout);
#endif
	cin>>n>>type;
	for(int i=1;i<=n;i++)
	{
		int a;scanf("%d",&a);
		for(int j=a-1;j;j-=j&-j)
			f[i]=max(f[i],Max[j]);
		if(f[i]==0)
			g[i]=1;
		else
			g[i]=Ask(rt[f[i]],0,N,a-1);
		f[i]++;
		if(f[i]>Ans1)
			Ans1=f[i],Ans2=0;
		if(f[i]==Ans1)
			Ans2=(Ans2+g[i])%Mod;
		Add(rt[f[i]],0,N,a,g[i]);
		for(int j=a;j<N;j+=j&-j)
			Max[j]=max(Max[j],f[i]);
	}
	cout<<Ans1<<endl;
	if(type)
		cout<<Ans2<<endl;
	return 0;
}//来自十里坡键神
posted @ 2018-10-18 13:20  迷失の风之旅人  阅读(220)  评论(0编辑  收藏  举报
123213123123