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 \times 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\n”,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
首先你需要离散化.这个看不出来就不怪我了啊
很容易发现,
这题要找满足这个条件的东西数量.
但是你看它枚举的顺序,先别在乎\(n^4\)
如果\(i==j\)依旧成立.即一个数可以和自己组合.
同时又可以和已经组合过的数组合.
然后如果有\(n\)个数的话,两两组合有多少个方案?建议手玩
即\(n \times (n-1) + n\)
\(+n\)即自己和自己组合的方案.
我竟然没看出来是\(n^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 值
正解贪心.
开两个堆分别维护
- \(ansx[i]\)代表在\(n\)行中选择最大值\(i\)次的答案.
- \(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;
}