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

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

很容易发现,

这题要找满足这个条件的东西数量.
\[
a[i]==a[j] &&a[i]<a[k] &&a[k]==a[l]
\]
但是你看它枚举的顺序,先别在乎\(n^4\)

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

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

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

即\(n \times (n-1) + n\)

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

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

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

显然现在的答案就是
\[
\sum_{i=1}^{cnt}num_i^2\times \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

时间: 2024-11-02 00:59:55

10.25模拟赛的相关文章

10.2模拟赛总结

10.2 模拟赛总结 T1. 数位dp: 一个非常非常非常非常显然的数位 DP \([L,R] = [1,R]-[1,L-1]\) 所以是分别求两次小于等于某个数字的方案数 \(f(i,j,k)\) 表示从低位数起的第 \(i\) 位,按照规则计算后答案为 \(j\quad (j=0,1)\) \(k\) 表示只考虑后面结尾和 \(lmt\)后面几位 的大小关系 \((k=0,1)\) 考虑第 \(i+1\) 位,算一下新构成的数字并判断下大小就可以了 注意到 \(L,R\) 数据范围特别大,需

10.22 模拟赛

10.22 模拟赛 T1 染色 考虑每个连通块删成一棵树就好了. mmp场上就我路径压缩写炸.... #include<iostream> #define MAXN 200006 using namespace std; int n , m; int fa[MAXN] , siz[MAXN] , book[MAXN] , sz[MAXN]; int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); } int main() {

10.31 模拟赛

10.31 模拟赛 A LIS 考虑每个数字前从 $ m $ 降序构造到 $ a_i $ 即可. #include <iostream> #include<algorithm> #include<cstring> #include<cstdio> #include<vector> using namespace std; #define MAXN 300006 int n , m , k; int A[MAXN]; vector<int&g

10.5模拟赛

这么多模拟赛都没整理,能整理一天算一天吧qaq T1题面 sol:应该不难吧,分别对横坐标和纵坐标取差的绝对值,易知:如果互质就可以看到,否则就不行.然后出题人很毒瘤要用unsigned long long. #include <cstdio> #include <algorithm> using namespace std; long long x1,y1,x2,y2,c1=0,c2=0; unsigned long long x,y; unsigned long long AB

10 01模拟赛订正

好吧,这是我第一次写模拟赛的订正,主要是有时间而且这次的题确实好... 第一题确实好,用的算法人人都会,就是看你能不能想到,我考只打了O(n^4)的暴力,最后还苦逼的MLE,爆零了... 暴力就不多说了...枚举两个点更新其他的点... 其实我考场上思考的是,能被标记的点都与其他的点有什么联系,可惜,除了模拟题目的做法,就不会了... 那让我们就认真地思考一发:我们设A(x1,x2),B(x2,c2),C(x3,c3)来更新D点,只有:有两个点横坐标相等,有两个点纵坐标相等,才可以更新出来一个新

10.1 模拟赛

由于算错了inf 又ak失败了 过于菜 T1 年轮蛋糕 loj 2758 题目大意: n个数构成的环 把这个环分成三段 使最小的最大 求这个最小段的和的最大值 思路: 可以想到二分 因为log方可以过 所以可以二分长度后lower_bound找断点 或者使用滑动窗口 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cma

10.27 模拟赛

这一次终极被吊打 甚至没进前十 T2 最后改错 T3 没写正解 T1 elim 题目大意: n 行 m 列的游戏棋盘,一行或一列上有连续 三个或更多的相同颜色的棋子时,这些棋子都被消除 当有多处可以被消除时,这些地方的棋子将同时被消除 求消除后的棋盘 思路: sb模拟 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cm

2019.10.24模拟赛赛后总结

本文原创,如果有不到位的地方欢迎通过右下角的按钮私信我! A.Icow Player 题目描述 被无止境的农活压榨得筋疲力尽后,Farmer John打算用他在MP3播放器市场新买的iCow来听些音乐,放松一下.FJ的iCow里存了N(1 <= N <= 1,000)首曲子,按1..N依次编号.至于曲子播放的顺序,则是按一个Farmer John自己设计的算法来决定: * 第i首曲子有一个初始权值R_i(1 <= R_i <= 10,000). * 当一首曲子播放完毕,接下来播放的

17.2.10 NOIP模拟赛 聪哥的工资

聪哥的工资 (money/money.in/money.out) 时限1000ms 内存256MB 题目描述 lwher: 了体验劳苦大众的生活,聪哥在暑假参加了打零工的活动,这个活动分为n个工作日,每个工作日的工资为Vi.有m个结算工钱的时间,聪哥可以自由安排这些时间,也就是说什么时候拿钱,老板说的不算,聪哥才有发言权! (因为聪哥是土豪,他是老板的老板,你觉得老板敢给聪哥安排任务吗?所以聪哥的工作就是看心情去拿钱拿完就走人啦...) 聪哥不喜欢身上一次性有太多的钱,于是他想安排一下拿钱的时间