模拟赛 10-25上午考试记

10-25上午考试记

NP(np)

Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 喜欢研究一些比较困难的问题,比如 np 问题。
这次它又遇到一个棘手的 np 问题。问题是这个样子的:有两个数 n 和 p,求 n 的阶乘
对 p 取模后的结果。
LYK 觉得所有 np 问题都是没有多项式复杂度的算法的,所以它打算求助即将要参加 noip
的你,帮帮 LYK 吧!

打表发现如果n>=p,答案就是0。

所以把n的范围缩小到1e7。但是有p=1e9+7。

分段打表就可以。 段长我设的1000000。

code:

#include <iostream>
#include <cstdio>

#define int long long

using namespace std;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

int n,p;

int a[117]={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};
/*
时间: 1e7 空间:1e2 
*/
signed main(){
	freopen("np.in","r",stdin);
	freopen("np.out","w",stdout);
	
	n=read(); p=read();
	if(n>=p){
		puts("0");
		return 0;
	}
	if(p==1000000007){
		int zmj=n/10000000;
		int ans=a[zmj];
		for(int i=zmj*10000000+1;i<=n;i++){
			ans=ans*i%p;
		}
		printf("%lld\n",ans%p);
		return 0;
	}
	int ans=1;
	for(int i=1;i<=n;i++){
		ans=ans*i%p;
	}
	printf("%lld\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

看程序写结果(program)

Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 最近在准备 NOIP2017 的初赛,它最不擅长的就是看程序写结果了,因此它拼命地
在练习。
这次它拿到这样的一个程序:
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[i]<a[k] && a[k]==a[l]) ans=(ans+1)%1000000007;
printf(“%d\n”,ans);

首先把这段代码复制就有20分。

优化。

把数学式子化简。

\[ans=\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^n\sum_{l=1}^n[a[i]== a[j]  and  a[i]<a[k]  and  a[k]==a[l]]\\=\sum_{i=1}^n\sum_{j=i+1}^n[t[a[i]]*t[a[j]]]\\=\sum_{i=1}^nt[a[i]]*\sum_{j=i+1}^nt[a[j]] \]

考虑到要开桶,所以去重离散化,一堆令人窒息的操作。

code:

#include <iostream>
#include <cstdio>
#include <algorithm>

#define int long long

using namespace std;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

const int mod=1000000007;

const int wx=500017;

int js[wx];
int a[wx],b[wx];
int t[wx];
int nxt[wx],pre[wx];

int n;
/*
时间: O(nlogn) 空间: 500000*6 22MB
*/
signed main(){
	freopen("program.in","r",stdin);
	freopen("program.out","w",stdout);
	
	n=read(); 
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];
	sort(b+1,b+1+n);
	int zmj=unique(b+1,b+1+n)-b;
	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+zmj,a[i])-b;
	for(int i=1;i<=n;i++)t[a[i]]++;
	sort(a+1,a+1+n);
	int ans=0;int last=0;
	for(int i=1;i<=n;i++){
		nxt[i]=n+1;
		if(a[i]!=a[i-1]){
			nxt[last]=i;
			last=i;
		}
	}
	last=n+1;
	for(int i=n;i>=1;i--){
		if(a[i]!=a[i+1]){
			pre[last]=i; 
			last=i;
		}
	}
	js[a[n]]=0; last=n;
	for(int i=pre[n];i>=1;i=pre[i]){
		js[a[i]]=js[a[last]]+(t[a[last]]*t[a[last]]%mod);
		js[a[i]]%=mod;
		last=i;
	}
	for(int i=1;i<=n;i=nxt[i]){
		ans=(ans+((t[a[i]]*t[a[i]])%mod*js[a[i]])%mod);
		ans%=mod;
	}
	printf("%lld\n",ans%mod);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

选数字 (select)

Time Limit:3000ms Memory Limit:64MB
题目描述
LYK 找到了一个 n*m 的矩阵,这个矩阵上都填有一些数字,对于第 i 行第 j 列的位置上
的数为 ai,j。
由于它 AK 了 noip2016 的初赛,最近显得非常无聊,便想到了一个方法自娱自乐一番。
它想到的游戏是这样的:每次选择一行或者一列,它得到的快乐值将会是这一行或者一列的
数字之和。之后它将该行或者该列上的数字都减去 p(之后可能变成负数)。如此,重复 k
次,它得到的快乐值之和将会是它 NOIP2016 复赛比赛时的 RP 值。
LYK 当然想让它的 RP 值尽可能高,于是它来求助于你。

先无脑暴力。

暴力之后感觉很无力啊。。。

那就贪心吧,最恶心的是行和列混在一起,明显有后效性啊。

那就强行把行和列拆开,分别处理出取i个行或j的最大答案。

还需要合并行和列的情况,那就枚举一个。

可以得到式子:

\[ans=max(ans,x[i]+y[k-i]-i*(k-i)*q) \]

为什么要减呢?因为我们处理出x和y两个数组是是互相独立的,也就是我们不考虑另一种情况。

所以这时的\(i\)行,\(k-i\)列都少减了对方所带来的一部分,那么就减去就可以了。

还有一个坑点是ans要附成极小值,中间变量还会炸int。

果然傻人有傻福,我直接\(int  ans=-1e17\)加上\(define  int  long  long\)就无脑过掉了这道题。

code:

#include <iostream>
#include <cstdio>
#include <queue>

#define int long long

using namespace std;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

const int wx=1500;

priority_queue<int > zmj1;
priority_queue<int > zmj2;

int mp[wx][wx];
int x[wx],y[wx];
int ansx[1000017],ansy[1000017];
int n,m,k,p,ans=-1e17;

void work1(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			mp[i][j]=read();
			x[i]+=mp[i][j];
			y[j]+=mp[i][j];
		}
	}
	int maxn=-0x3f3f3f3f;
	for(int i=1;i<=n;i++)maxn=max(maxn,x[i]);
	for(int i=1;i<=m;i++)maxn=max(maxn,y[i]);
	if(k==1)printf("%lld\n",maxn);
	else if(p==0)printf("%lld\n",maxn*k);
	return ;
}


void dfs(int step,int tot){
	if(step==k+1){
		ans=max(ans,tot);
		return ;
	}
	for(int i=1;i<=n;i++){
		int tmp=0;
		for(int j=1;j<=m;j++){
			tmp+=mp[i][j];
			mp[i][j]-=p;
		}
		dfs(step+1,tot+tmp);
		for(int j=1;j<=m;j++)
			mp[i][j]+=p;
	}
	for(int i=1;i<=m;i++){
		int tmp=0;
		for(int j=1;j<=n;j++){
			tmp+=mp[j][i];
			mp[j][i]-=p;
		}
		dfs(step+1,tot+tmp);
		for(int j=1;j<=n;j++){
			mp[j][i]+=p;
		}
	}
}

void work2(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			mp[i][j]=read();
		dfs(1,0);
	printf("%lld\n",ans);
} 

void work3(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			mp[i][j]=read();
			x[i]+=mp[i][j];
			y[j]+=mp[i][j];
		}
	}
	for(int i=1;i<=n;i++)zmj1.push(x[i]);
	for(int i=1;i<=m;i++)zmj2.push(y[i]);
	for(int i=1;i<=k;i++){
		int tmp=zmj1.top(); zmj1.pop();
		ansx[i]=ansx[i-1]+tmp;
		zmj1.push(tmp-p*m);
		tmp=zmj2.top(); zmj2.pop();
		ansy[i]=ansy[i-1]+tmp;
		zmj2.push(tmp-p*n);
	}
	for(int i=0;i<=k;i++){
		ans=max(ans,ansx[i]+ansy[k-i]-i*(k-i)*p);
	}
	printf("%lld\n",ans);
	return;
}
/*
时间:O(ologn) 空间: 4500000 32MB 
*/
signed main(){
	freopen("select.in","r",stdin);
	freopen("select.out","w",stdout);
	
	n=read(); m=read(); k=read(); p=read();
	if(k==1||p==0){
		work1();
		fclose(stdin);
		fclose(stdout);
		return 0;
	}
	else if(n<=5&&m<=5&&k<=5){
		work2();
		fclose(stdin);
		fclose(stdout);
		return 0;
	}
	else{
		work3();
		fclose(stdin);
		fclose(stdout);
		return 0;
	}
	
}

总结:

T1考的打表。主要是分段打表看谁用的好吧。。

T2细节蛮多,但是不难。

T3思维题,这几天的考试都告诉我T3不要上来就想比较难的算法,可能那不是正解,要好好思考一下一些基础算法是否适用于这道题,可能基础算法才是正解。

垃圾小呆上午AK,静心等待下午爆炸。。

posted @ 2018-10-25 11:46  _王小呆  阅读(176)  评论(0编辑  收藏  举报