模拟赛 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,静心等待下午爆炸。。

猜你喜欢

转载自www.cnblogs.com/wangxiaodai/p/9848922.html
今日推荐