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,静心等待下午爆炸。。
原文地址:https://www.cnblogs.com/wangxiaodai/p/9848922.html