NP(np)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 喜欢研究一些比较困难的问题,比如 np 问题。
这次它又遇到一个棘手的 np 问题。问题是这个样子的:有两个数 n 和 p,求 n 的阶乘
对 p 取模后的结果。
LYK 觉得所有 np 问题都是没有多项式复杂度的算法的,所以它打算求助即将要参加 noip
的你,帮帮 LYK 吧!
输入格式(np.in)
输入一行两个整数 n,p。
输出格式(np.out)
输出一行一个整数表示答案。
输入样例
3 4
输出样例
2
数据范围
对于 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。
思路:
分块打表
打表出所有k!,其中k mod 10000000=0。询问时直接找到已知的答案,并计算不超过10000000就能找到答案。
代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std ;
long long n,p;
long long now;
const int a[100 ]={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 };
const int MOD=1000000007 ;
int main()
{
freopen("np.in" ,"r" ,stdin);
freopen("np.out" ,"w" ,stdout);
cin >>n>>p;
if (p==1000000007 )
{
if (n>=p) {puts ("0" ); return 0 ;}if (n<10000000 ) now=1 ; else now=a[n/10000000 -1 ];
for (int i=n/10000000 *10000000 +1 ; i<=n; i++) now=now*i%MOD;
} else
{
now=1 ;
if (n>=p) now=0 ; else for (int i=1 ; i<=n; i++) now=now*i%p;
}
cout <<now<<endl;
return 0 ;
}
看程序写结果(program)
Time Limit:1000ms Memory Limit:64MB
题目描述
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++:
scanf(“%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\n”,ans);
LYK 知道了所有输入数据,它想知道这个程序运行下来会输出多少。
输入格式(program.in)
第一行一个数 n,第二行 n 个数,表示 ai。
输出格式(program.out)
一个数表示答案。
输入样例
4
1 1 3 3
输出样例
4
数据范围
对于 20%的数据 n<=50。
对于 40%的数据 n<=200。
对于 60%的数据 n<=2000。
对于 100%的数据 n<=100000, 1<=ai<=1000000000。
其中均匀分布着 50%的数据不同的 ai 个数<=10,对于另外 50%的数据不同的 ai 个数>=n/10。
思路:
利用了乘法原理
先将ai进行排序。
令f[i]表示1~i中相同的ai对数。
对于所有ai!=a{i+1}的位置,将f[i]*g*(g-1)/2累加进答案中。其中g表示存在多少数字=a{i+1}。
时间复杂度nlgn。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long ans,f[100005 ];
int i,n,a[100005 ],sum ;
const int MOD=1000000007 ;
int cmp(int i,int j) {return i<j;}
int main()
{
freopen("program.in" ,"r" ,stdin);
freopen("program.out" ,"w" ,stdout);
scanf("%d" ,&n);
for (i=1 ; i<=n; i++) scanf("%d" ,&a[i]);
sort(a+1 ,a+n+1 ,cmp); sum =1 ; f[1 ]=1 ;
for (i=2 ; i<=n; i++)
{
f[i]=f[i-1 ];
if (a[i]==a[i-1 ])
{
f[i]=f[i]-1 ll*sum *sum ; sum ++;
f[i]=f[i]+1 ll*sum *sum ;
f[i]%=MOD;
} else {sum =1 ; f[i]++;}
}
sum =0 ;
for (i=n; i>=2 ; i--)
{
if (a[i]==a[i+1 ]) sum ++; else sum =1 ;
if (a[i]!=a[i-1 ]) ans=(ans+f[i-1 ]*sum %MOD*sum )%MOD;
}
cout<<ans<<endl;
return 0 ;
}
选数字 (select)
Time Limit:3000ms Memory Limit:64MB
题目描述
LYK 找到了一个 n*m 的矩阵,这个矩阵上都填有一些数字,对于第 i 行第 j 列的位置上
的数为 ai,j。
由于它 AK 了 noip2016 的初赛,最近显得非常无聊,便想到了一个方法自娱自乐一番。
它想到的游戏是这样的:每次选择一行或者一列,它得到的快乐值将会是这一行或者一列的
数字之和。之后它将该行或者该列上的数字都减去 p(之后可能变成负数)。如此,重复 k
次,它得到的快乐值之和将会是它 NOIP2016 复赛比赛时的 RP 值。
LYK 当然想让它的 RP 值尽可能高,于是它来求助于你。
输入格式(select.in)
第一行 4 个数 n,m,k,p.
接下来 n 行 m 列,表示 ai,j。
输出格式(select.out)
输出一行表示最大 RP 值。
输入样例
2 2 5 2
1 3
2 4
输出样例
11
数据范围
总共 10 组数据。
对于第 1,2 组数据 n,m,k<=5。
对于第 3 组数据 k=1。
对于第 4 组数据 p=0。
对于第 5,6 组数据 n=1, m,k<=1000。
对于第 7,8 组数据 n=1, m<=1000, k<=1000000。
对于所有数据 1<=n,m<=1000, k<=1000000, 1<=ai,j<=1000, 0<=p<=100。
样例解释
第一次选择第二列,第二次选择第二行,第三次选择第一行,第四次选择第二行,第五
次选择第一行,快乐值为 7+4+2+0+-2=11。
思路:
选择顺序无关紧要,假设我们选了x行,那么必然选择了k-x列。
令f[i]表示选择i行得到的最大和。考虑怎么求出所有f[i]。
这个是显然可以贪心的。记录所有行的总和。利用大根对维护即可。
令g[i]表示选择i列得到的最大和。已知f与g后。
答案为max{f[i]+g[k-i]-i*(k-i)*p)。
代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std ;
const int N=1000005 ;
long long ans,s1[N],s2[N],p1[N],p2[N];
int k,n,m,i,j,p,A;
int cmp(int i,int j) {return i<j;}
int main()
{
freopen("select.in" ,"r" ,stdin);
freopen("select.out" ,"w" ,stdout);
scanf ("%d%d%d%d" ,&n,&m,&k,&p);
for (i=1 ; i<=n; i++)
for (j=1 ; j<=m; j++)
{
scanf ("%d" ,&A);
s1[i]+=A; s2[j]+=A;
}
make_heap(s1+1 ,s1+n+1 ,cmp);
make_heap(s2+1 ,s2+m+1 ,cmp);
for (i=1 ; i<=k; i++)
{
p1[i]=p1[i-1 ]+s1[1 ];
pop_heap(s1+1 ,s1+n+1 ,cmp);
s1[n]-=p*m;
push_heap(s1+1 ,s1+n+1 ,cmp);
p2[i]=p2[i-1 ]+s2[1 ];
pop_heap(s2+1 ,s2+m+1 ,cmp);
s2[m]-=p*n;
push_heap(s2+1 ,s2+m+1 ,cmp);
}
ans=1l l*-1000000000 *1000000000 ;
for (i=0 ; i<=k; i++) {ans=max(ans,p1[i]+p2[k-i]-1l l*i*(k-i)*p);}
cout <<ans;
return 0 ;
}