10.25模拟赛

NP(np)

Description

LYK 喜欢研究一些比较困难的问题, 比如 np 问题。

这次它又遇到一个棘手的 np 问题。 问题是这个样子的: 有两个数 n 和 p, 求 n 的阶乘 对 p 取模后的结果。

LYK 觉得所有 np 问题都是没有多项式复杂度的算法的,所以它打算求助即将要参加 noip 的你, 帮帮 LYK 吧!

Input

​ 输入一行两个整数 n,p。

Output

输出一行一个整数表示答案。

数据范围

对于 20%的数据: n,p<=5。

对于 40%的数据: n,p<=1000。

对于 60%的数据: n,p<=10000000。

对于 80%的数据: n<=10^18, p<=10000000。

对于另外 20%的数据: n<=10^18, p=1000000007。

其中大致有 50%的数据满足 n>=p

很显然的是,当(n geq p)的时候,直接输出(0)

此时,求解(n)的阶乘的时候会乘上一个(p)

则代表(n=k imes p)

然后呢,其他部分的分怎么得?

分段打表!

我们打出(10^7)(10^9)部分的阶乘(%1000000007)的值.

然后遇到(p=1000000007)的情况的时候,我们可以直接跳到这一倍数位置.后面部分暴力扩展即可.

复杂度(O(能够))

还玩了半天(python) ~w~

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cctype>
#define int long long 
#define R register
using namespace std;
inline void in(int &x)
{
	int f=1;x=0;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
	x*=f;
}
int n,p,res=1;
const int fac[]=
{
	1,682498929,491101308,76479948,723816384,67347853,27368307,625544428,199888908,888050723,927880474,281863274,661224977,623534362,
	970055531,261384175,195888993,66404266,547665832,109838563,933245637,724691727,368925948,268838846,136026497,112390913,135498044,217544623,
	419363534,500780548,668123525,128487469,30977140,522049725,309058615,386027524,189239124,148528617,940567523,917084264,429277690,996164327,
	358655417,568392357,780072518,462639908,275105629,909210595,99199382,703397904,733333339,97830135,608823837,256141983,141827977,696628828,
	637939935,811575797,848924691,131772368,724464507,272814771,326159309,456152084,903466878,92255682,769795511,373745190,606241871,825871994,
	957939114,435887178,852304035,663307737,375297772,217598709,624148346,671734977,624500515,748510389,203191898,423951674,629786193,672850561,
	814362881,823845496,116667533,256473217,627655552,245795606,586445753,172114298,193781724,778983779,83868974,315103615,965785236,492741665,
	377329025,847549272,698611116
};
signed main()
{
	freopen("np.in","r",stdin);
	freopen("np.out","w",stdout);
	in(n),in(p);
	if(n>=p){puts("0");return 0;}
	if(p==1000000007)
	{
		int now=n/10000000;
		int res=fac[now];
		for(R int i=now*10000000+1;i<=n;res*=i,res%=p,i++);
		printf("%lld",(res<0)?(res+p)%p:res%p);
		return 0;
	}
	for(R int i=1;i<=n;res*=i,res%=p,i++);
	printf("%lld",(res<0)?(res+p)%p:res%p);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

看程序写结果(program)

Description

LYK 最近在准备 NOIP2017 的初赛, 它最不擅长的就是看程序写结果了, 因此它拼命地 在练习。

这次它拿到这样的一个程序:

Pascal:
readln(n);
for i:=1 to n do read(a[i]);
for i:=1 to n do for j:=1 to n do for k:=1 to n do for l:=1 to n do
if (a[i]=a[j]) and (a[i]<a[k]) and (a[k]=a[l]) then ans:=(ans+1) mod 1000000007;
writeln(ans);
C++:
pcanf(“%d”,&n);
for (i=1; i<=n; i++) scanf(“%d”,&a[i]);
for (i=1; i<=n; i++) for (j=1; j<=n; j++) for (k=1; k<=n; k++) for (l=1; l<=n; l++)
if (a[i]==a[j] && a[i]<a[k] && a[k]==a[l]) ans=(ans+1)%1000000007;
printf(“%d
”,ans)

LYK 知道了所有输入数据, 它想知道这个程序运行下来会输出多少。

Input

第一行一个数 n, 第二行 n 个数, 表示 ai。

Output

一个数表示答案。

数据范围

数据范围

对于 20%的数据 n<=50。

对于 40%的数据 n<=200。

对于 60%的数据 n<=2000。

对于 100%的数据 n<=100000, 1<=ai<=1000000000

其中均匀分布着 50%的数据不同的 ai 个数<=10, 对于另外 50%的数据不同的 ai 个 数>=n/10。

这题得分最简单了把上面代码粘下来都有40pts

首先你需要离散化.这个看不出来就不怪我了啊

很容易发现,

这题要找满足这个条件的东西数量.

[a[i]==a[j] &&a[i]<a[k] &&a[k]==a[l] ]

但是你看它枚举的顺序,先别在乎(n^4)

如果(i==j)依旧成立.即一个数可以和自己组合.

同时又可以和已经组合过的数组合.

然后如果有(n)个数的话,两两组合有多少个方案?建议手玩

(n imes (n-1) + n)

(+n)即自己和自己组合的方案.

我竟然没看出来是(n^2),然后直接写了这个式子.

代码还取了很多模,可丑了

显然现在的答案就是

[sum_{i=1}^{cnt}num_i^2 imes sum_{j=i+1}^{cnt}num_j^2 ]

代码中并不是这样写的.忘了化简式子但是是一样的.

然后我们可以维护一个后缀和,达到(O(n))求解.

总复杂度(O(nlogn))

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cctype>
#define N 100008
#define ll long long
#define mod 1000000007
#define R register
using namespace std;
inline void in(int &x)
{
	int f=1;x=0;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
	x*=f;
}
int n,a[N],b[N];
int tong[N],mx,cnt=1;
ll ans,sum[N];
int main()
{
	freopen("program.in","r",stdin);
	freopen("program.out","w",stdout);
	in(n);
	for(R int i=1;i<=n;i++)
		in(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	for(R int i=2;i<=n;i++)
		if(b[i]!=b[cnt])b[++cnt]=b[i];
	for(R int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
		(tong[a[i]]+=1)%=mod;
	}
	for(R int i=cnt;i>=1;i--)
		sum[i]=(sum[i+1]%mod+(((tong[i]%mod)*((tong[i]-1)%mod)%mod)*1LL+(tong[i]%mod)*1LL))%mod;
	for(R int i=1;i<cnt;i++)
	{
		R ll tmp=(((tong[i]%mod)*((tong[i]-1)%mod)*1LL)+tong[i])*1LL;
		(ans+=tmp*sum[i+1])%=mod;
	}
	printf("%lld",(ans<0) ? (ans+mod)%mod:ans%mod);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

选数字(select)

Description

LYK 找到了一个 n*m 的矩阵, 这个矩阵上都填有一些数字, 对于第 i 行第 j 列的位置上 的数为 ai,j。

由于它 AK 了 noip2016 的初赛, 最近显得非常无聊, 便想到了一个方法自娱自乐一番。 它想到的游戏是这样的: 每次选择一行或者一列, 它得到的快乐值将会是这一行或者一列的 数字之和。 之后它将该行或者该列上的数字都减去 p(之后可能变成负数)。 如此, 重复 k 次, 它得到的快乐值之和将会是它 NOIP2016 复赛比赛时的 RP 值。

LYK 当然想让它的 RP 值尽可能高, 于是它来求助于你

Input

第一行 4 个数 n,m,k,p。

接下来 n 行 m 列, 表示 ai,j

Output

输出一行表示最大 RP 值

正解贪心.

开两个堆分别维护

  1. (ansx[i])代表在(n)行中选择最大值(i)次的答案.
  2. (ansy[i])代表在(m)列中选择最大值(i)次的答案.

然后枚举选(i)次行,那么我们就知道选(k-i)次列.

但是我们重复减去了一些东西,再加上就好了,应该不是很难理解.就不多BB了

注意极小值一定要够小!!!

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<cctype>
#define int long long 
#define R register
using namespace std;
inline void in(int &x)
{
	int f=1;x=0;char s=getchar();
	while(!isdigit(s)){if(s=='-')f=-1;s=getchar();}
	while(isdigit(s)){x=x*10+s-'0';s=getchar();}
	x*=f;
}
int n,m,k,p;
int mx=-2147483644666666LL;
int xx[1008],yy[1008];
priority_queue<int>q;
priority_queue<int>Q;
int ansx[1000008],ansy[1000008],ans;
inline int get()
{
	R int res=-2147483644666666LL,pos,flg;
	for(R int i=1;i<=n;i++)
		if(xx[i]>res)res=xx[i]*1LL,flg=1,pos=i;
	for(R int j=1;j<=m;j++)
		if(yy[j]>res)res=yy[j]*1LL,flg=2,pos=j;
	if(flg==1)
	{
		for(R int i=1;i<=m;i++)
			yy[i]-=p;
		xx[pos]-=m*p;
	}
	if(flg==2)
	{ 
		for(R int i=1;i<=n;i++)
			xx[i]-=p;
		yy[pos]-=n*p;
	}
	return res;
}
signed main()
{
	freopen("select.in","r",stdin);
	freopen("select.out","w",stdout);
	
	in(n),in(m),in(k),in(p);
	
	if(n<=5 and m<=5 and k<=5)
	{
		for(R int i=1;i<=n;i++)
			for(R int j=1,x;j<=m;j++)
				in(x),xx[i]+=x,yy[j]+=x;
		for(R int i=1;i<=k;i++)ans+=get();
		printf("%lld",ans);
		return 0;
	}//20pts;
	
	
	if(p==0 or k==1)
	{
		for(R int i=1;i<=n;i++)
			for(R int j=1,x;j<=m;j++)
				in(x),xx[i]+=x,yy[j]+=x;
		for(R int i=1;i<=n;i++)mx=max(1LL*xx[i],mx);
		for(R int i=1;i<=m;i++)mx=max(1LL*yy[i],mx);		
		printf("%lld",mx*(k*1LL));
		return 0;
	}//20pts;
	
	
	for(R int i=1;i<=n;i++)
		for(R int j=1,x;j<=m;j++)
			in(x),xx[i]+=x,yy[j]+=x;
	for(R int i=1;i<=n;i++)q.push(1LL*xx[i]);
	for(R int i=1;i<=m;i++)Q.push(1LL*yy[i]);
	for(R int i=1;i<=k;i++)
	{
		R int x=q.top();
		q.pop();
		ansx[i]=ansx[i-1]+x;
		q.push(x-p*m);
		
		R int y=Q.top();
		Q.pop();
		ansy[i]=ansy[i-1]+y;
		Q.push(y-p*n);
	}
	ans=-2147483644666666LL;
	for(int i=0;i<=k;i++)
		ans=max(ans,ansx[i]+ansy[k-i]-i*(k-i)*p);
	printf("%lld",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/-guz/p/9848827.html