1. 程式人生 > >2017/9/13模擬賽

2017/9/13模擬賽

enter iostream 輸入 正整數 是個 解釋 () stdin 命令

粉飾(decorate)

【題目描述】

小D有一塊被分為n*m個格子的矩形魚片。為了裝飾魚片,小D決定給每個格子上色。由於小D很喜歡紅白,所以小D給每個格子塗上了紅色或白色,第i行第j列的格子顏色記為c[i,j]。塗完之後,小D想評估這塊魚片的“XY值”。我們定義一個有序無重復三元格子組{(x1,y1),(x2,y2),(x3,y3)}為“XY組”當且僅當:

|(x1-x2)*(y1-y2)|+|(x3-x2)*(y3-y2)|=0

(c[x1,y1]-c[x2,y2])*(c[x3,y3]-c[x2,y2])≠0

一塊魚片的“XY值”為該塊魚片裏“XY組”的數量。

【輸入數據】

第一行兩個正整數n,m。

為描述整塊魚片,接下來n行,每行一個長度為m的01串,0表示白色,1表示紅色。

【輸出數據】

輸出一行,一個整數表示這塊魚片的“XY值”。

【樣例輸入】

3 3

011

100

011

【樣例輸出】

44

【數據範圍】

本題采用子任務制。

Subtask 1(20pts):1<=n,m<=100;

Subtask 2(10pts):n=1;

Subtask 3(20pts):n=3;

Subtask 4~5(各25pts) 沒有數據範圍限制;

對於100%的數據,1<=n*m<=4*10^6,0<=c[i][j]<=1。

【樣例解釋】

由於本題比較特殊,所以沒有樣例解釋。

題解:預處理行列的0,1個數,枚舉x2即可。

代碼如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 int n,m;char x;
 6 long long ans,ans2,k;
 7 void add(){if(ans>=1e18) ans2++,ans-=1e18;}
 8 int main(){
 9     freopen("decorate.in","r",stdin);
10     freopen("
decorate.out","w",stdout); 11 scanf("%d%d",&n,&m); 12 int a[n+5][m+5],h0[n+5],h1[n+5],l0[m+5],l1[m+5]; 13 memset(h0,0,sizeof(h0));memset(h1,0,sizeof(h1)); 14 memset(l0,0,sizeof(l0));memset(l1,0,sizeof(l1)); 15 for(int i=1;i<=n;i++){ 16 scanf("%c",&x); 17 for(int j=1;j<=m;j++){ 18 scanf("%c",&x); 19 a[i][j]=x-0; 20 if(a[i][j]){h1[i]++;l1[j]++;} 21 else{h0[i]++;l0[j]++;} 22 } 23 } 24 for(int i=1;i<=n;i++) 25 for(int j=1;j<=m;j++){ 26 if(a[i][j]) k=h0[i]+l0[j]; 27 else k=h1[i]+l1[j]; 28 ans+=k*(k-1); add(); 29 } 30 if(ans2) printf("%lld%018lld",ans2,ans); 31 else printf("%lld",ans); 32 return 0; 33 }

洞悉(insight)

【題目描述】

在走出了第6扇門後,小I終於可以使用他之前獲得的水晶球了。當他透過水晶球看向前方,發現門的後面,是一扇又一扇無盡的門。n個房間排在一起,筆直地延伸向遠方。為了讓自己接下來的體驗不算太差,小I想知道這n個房間中其中一些房間的信息,並進行一些修改。每個房間都有一個seed值。而小I有兩種操作:

1 x y:詢問[x,y]區間的房間的seed值的乘積對1000000007的模;

2 l r:將[l,r]區間裏所有房間的seed值改為φ(seed)

其中,φ(x)為歐拉函數,即小等於x的與x互質的數的個數

【輸入數據】

第一行兩個正整數n,m。

第二行n個正整數,表示每個房間的seed值。

接下來m行,每行表示一個小I的操作。

【輸出數據】

對於每個操作1,輸出一行詢問的答案。

【樣例輸入】

5 6

1 2 4 8 9

1 1 2

2 2 4

2 1 3

1 1 5

1 2 3

2 3 5

【樣例輸出】

32

4

24

【數據範圍】

對於20%的數據,n,m<=1000;

對於40%的數據,n,m<=50000;

另外20%的數據,seed<=100000。

對於100%的數據,1<=n,m<=200000,1<=seed<=10^7。

【樣例解釋】

下面給出每次操作得到的結果:

①序列變為1 1 4 8 9

②1*4*8=32

③1*1*4=4

④序列變為1 1 2 4 6

⑤序列變為1 1 1 4 6

⑥1*4*6=24

題解:用線段樹維護區間乘積和區間最大值,若區間最大值為1,則再取歐拉函數值也不會改變返回即可,可以證明,一個數取歐拉函數最多log(這個數)的步數,在這之前,我們預處理出範圍內所有數的歐拉函數,用篩法實現。

代碼如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #define Mod 1000000007
 5 #define MN 200005
 6 #define MS 10000005
 7 using namespace std;
 8 int n,m,phi[MS],pri[MS],cnt,a[MN];
 9 struct node{int pr,mx;}t[MN*3];
10 void update(int k){
11     t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx);
12     t[k].pr=1LL*t[k<<1].pr*t[k<<1|1].pr%Mod;
13 }
14 void build(int k,int l,int r){
15     if(l==r){t[k].mx=t[k].pr=a[l]; return;}
16     int mid=l+r>>1;
17     build(k<<1,l,mid); build(k<<1|1,mid+1,r);
18     update(k);
19 }
20 int getpro(int k,int l,int r,int ql,int qr){
21     if(ql==l&&qr==r) return t[k].pr;
22     int mid=l+r>>1;
23     if(qr<=mid) return getpro(k<<1,l,mid,ql,qr);
24     else if(ql>mid) return getpro(k<<1|1,mid+1,r,ql,qr);
25     else return 1LL*getpro(k<<1,l,mid,ql,mid)*getpro(k<<1|1,mid+1,r,mid+1,qr)%Mod;
26 }
27 void getdown(int k,int l,int r){
28     if(t[k].mx==1) return;
29     if(l==r){t[k].mx=t[k].pr=phi[t[k].mx]; return;}
30     int mid=l+r>>1;
31     getdown(k<<1,l,mid); getdown(k<<1|1,mid+1,r);
32     update(k);
33 }
34 void getphi(int k,int l,int r,int ql,int qr){
35     if(ql==l&&qr==r){getdown(k,l,r); return;}
36     int mid=l+r>>1;
37     if(qr<=mid) getphi(k<<1,l,mid,ql,qr);
38     else if(ql>mid) getphi(k<<1|1,mid+1,r,ql,qr);
39     else getphi(k<<1,l,mid,ql,mid),getphi(k<<1|1,mid+1,r,mid+1,qr);
40     update(k);
41 }
42 int main()
43 {
44     phi[1]=1;
45     for(int i=2;i<MS;i++){
46         if(!phi[i]) pri[++cnt]=i,phi[i]=i-1;
47         //i是個素數,i的fai=i-1 
48         for(int j=1;i*pri[j]<MS;j++)
49             if(i%pri[j]) phi[i*pri[j]]=phi[i]*phi[pri[j]];
50             //如果a,b互質,則a*b的歐拉函數=a的歐拉函數*b的歐拉函數 
51             else{phi[i*pri[j]]=phi[i]*pri[j]; break;}
52             //如果b是素數,a是b的倍數,則a*b的fai=a的fai*b
53     }
54     scanf("%d%d",&n,&m);
55     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
56     build(1,1,n);
57     while(m--){
58         int op,x,y;
59         scanf("%d%d%d",&op,&x,&y);
60         if(op==1) getphi(1,1,n,x,y);
61         else printf("%d\n",getpro(1,1,n,x,y));
62     }
63     return 0;
64 }

命令(order)

【題目描述】

小O開了許多年飛機,現在她準備更換自己的炮臺。於是就有很多炮臺來應聘。為了選拔最優秀的炮臺,小O給炮臺們下了一條指令,要求他們在n個數中,選出若幹個數,使得它們兩兩之間的和不為質數,最後使得這些數的乘積盡可能大。作為一名優秀的炮臺,為了使自己處於尷尬的境地,你需要又快又好地解決這個問題。

【輸入數據】

第一行一個正整數n。

第二行n個正整數a1~an,表示小O給出的數字。

【輸出數據】

輸出一行表示最大乘積,答案對10^9+7取模。

【樣例輸入】

6

3 2 2 3 4 4

【樣例輸出】

64

【數據範圍】

本題采用子任務制。

Subtask 1(10pts):n<=13;

Subtask 2(12pts):n<=23;

Subtask 3(13pts):ai<=20;

Subtask 4(15pts):ai<=2000;

Subtask 5~6(各25pts):沒有數據範圍限制;

對於100%的數據,1<=n<=1000,1<=ai<=5*10^5。

【樣例解釋】

選取2、2、4、4四個數,2*2*4*4=64。

2017/9/13模擬賽