1. 程式人生 > >9.15模擬賽

9.15模擬賽

ont 貪心 ifd 模擬 clu printf write 答案 ber

T1 np問題

題目描述

LYK喜歡研究一些比較困難的問題,比如np問題。

這次它又遇到一個棘手的np問題。問題是這個樣子的:有兩個數n和p,求n的階乘對p取模後的結果。

LYK覺得所有np問題都是沒有多項式復雜度的算法的,所以它打算求助即將要參加noip的你,幫幫LYK吧!

輸入輸出格式

輸入格式:

輸入一行兩個整數n,p。

輸出格式:

輸出一行一個整數表示答案。

輸入輸出樣例

輸入樣例#1:
3 4
輸出樣例#1:
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。

題解:

如果n>=p輸出0,如果n<=10000000直接跑,如果n<p<=1000000007打表。

分段打表 每段 10^7。

第一題煞筆了,%到0才break,直接判斷輸出0不就行了....50分都沒有...Q^Q

老師還手動證明了快速乘比直接乘慢....my heart is flowing blood

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#ifdef unix
#define LL "%lld" #else #define LL "%I64d" #endif long long n,p,ans=1; inline long long read(){ char ch=getchar();long long x=0,f=1; for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1; for(;isdigit(ch);ch=getchar())x=x*10+ch-0; return x*f; } long long num[120]={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}; int main(){ n=read();p=read(); if(n>=p){ printf("0\n"); return 0; } if(p==1000000007){ ans=num[n/10000000]; for(int i=n/10000000*10000000+1;i<=n;i++)ans=ans*i%p; cout<<ans%p<<endl; return 0; } for(int i=1;i<=n;i++) ans=ans*i%p; cout<<ans%p<<endl; return 0; }

T2

看程序寫結果

題目描述

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知道了所有輸入數據,它想知道這個程序運行下來會輸出多少。

輸入輸出格式

輸入格式:

第一行一個數n,第二行n個數,表示ai。

輸出格式:

一個數表示答案。

輸入輸出樣例

輸入樣例#1:
4
1 1 3 3
輸出樣例#1:
4

說明

對於20%的數據n<=50。

對於40%的數據n<=200。

對於60%的數據n<=2000。

對於100%的數據n<=100000,1<=ai<=1000000000。

題解:

直接把程序搬上去20分。

正解:首先看第一層和第三層循環,作用是尋找和它一樣的數有多少個。我們可以記錄一下每個數

有多少個,在平方就是前兩層循環找的方案數,然後用前綴和記錄,每個數乘以比它大的個數。

代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define maxn 100005
#define mod 1000000007LL
using namespace std;

int a[maxn],n,cnt;
long long num[maxn],sum[maxn],ans;

inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-0;
    return x*f;
}

int main(){
    n=read();
    for(int i=1;i<=n;i++)a[i]=read();
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(a[i]!=a[i-1])cnt++;
        num[cnt]++;
    }
    for(int i=1;i<=cnt;i++){
        num[i]=(num[i]*num[i])%mod;
        sum[i]=sum[i-1]+num[i];
    }
    for(int i=1;i<cnt;i++){
        ans=(ans+num[i]*(sum[cnt]-sum[i]))%mod;
    }
    cout<<ans<<endl;
    return 0;
}

T3 選數字

題目描述

LYK找到了一個n*m的矩陣,這個矩陣上都填有一些數字,對於第i行第j列的位置上的數為ai,j。

由於它AK了noip2016的初賽,最近顯得非常無聊,便想到了一個方法自娛自樂一番。它想到的遊戲是這樣的:每次選擇一行或者一列,它得到的快樂值將會是這一行或者一列的數字之和。之後它將該行或者該列上的數字都減去p(之後可能變成負數)。如此,重復k次,它得到的快樂值之和將會是它NOIP2016復賽比賽時的RP值。

LYK當然想讓它的RP值盡可能高,於是它來求助於你。

輸入輸出格式

輸入格式:

第一行4個數n,m,k,p。

接下來n行m列,表示ai,j。

輸出格式:

輸出一行表示最大RP值。

輸入輸出樣例

輸入樣例#1:
2 2 5 2
1 3
2 4
輸出樣例#1:
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。

其中均勻分布著50%的數據不同的ai個數<=10,對於另外50%的數據不同的ai個數>=n/10。

題目大意:每次選矩陣的某一行或者是某一列,得到列或者行的和,然後每個數-p,重復q次。

題解:40分 特判+暴力鬼畜貪心。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

int n,m,k,p,maxn,x;
long long ans;

inline int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-0;
    return x*f;
}

struct CC{
    int sum,id;
}col[1002];

struct RR{
    int sum,id;
}row[1002];

bool cmpc(CC a,CC b){
    return a.sum>b.sum;
}

bool cmpr(RR a,RR b){
    return a.sum>b.sum;
}

int main(){
    freopen("select.in","r",stdin);
    freopen("select.out","w",stdout);
    n=read();m=read();k=read();p=read();
    //n行 m列 k重復次數  減去p
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            x=read();
            row[i].id=i;row[i].sum+=x;
            col[j].id=j;col[j].sum+=x;
            maxn=max(maxn,max(row[i].sum,col[j].sum));
        }
    } 
    
    if(k==1){printf("%d\n",maxn);return 0;}
    if(p==0){cout<<1LL*k*maxn;return 0;}
    sort(row+1,row+n+1,cmpr);
    sort(col+1,col+m+1,cmpc);
    for(int i=1;i<=k;i++){
        if(row[1].sum>col[1].sum){
            ans+=row[1].sum;
            row[1].sum-=(p*m);
            for(int i=1;i<=m;i++)col[i].sum-=p;
        }else{
            ans+=col[1].sum;
            col[1].sum-=(p*m);
            for(int i=1;i<=n;i++)row[i].sum-=p;
        }
        sort(row+1,row+n+1,cmpr);
        sort(col+1,col+m+1,cmpc);
    }
    cout<<ans<<endl;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

正解:貪心+枚舉

發現每次取行或者是列都是獨立的。預處理出行的前k大值,列的前k大值。

枚舉行取多少個,列就是k-行的個數,最後再減去沒有減的p。

代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n,m,k,p;
long long ans;
long long  row[1010],col[1010],sum1[1010000],sum2[1010000];
inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-0;
    return x*f; 
}

int main(){
    n=read();m=read();k=read();p=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            int x;x=read();    
            row[i]+=x;col[j]+=x;    
        }
    }
    make_heap(row+1,row+n+1);
    make_heap(col+1,col+m+1);
    for(int i=1;i<=k;i++){
        sum1[i]=sum1[i-1]+row[1];
        pop_heap(row+1,row+n+1);
        row[n]-=m*p;
        push_heap(row+1,row+n+1);
        sum2[i]=sum2[i-1]+col[1];
        pop_heap(col+1,col+m+1);
        col[m]-=n*p;
        push_heap(col+1,col+m+1);
    }
    ans=1LL*-100000000*1000000000;
    for(int i=0;i<=k;i++)ans=max(ans,sum1[i]+sum2[k-i]-1LL*i*(k-i)*p);
    cout<<ans<<endl;
    return 0;
}

9.15模擬賽