小X 歸來後,首先對數列很感興趣。他想起有1類特殊的數列叫單峯數列。 咱們說一個數列 {ai} 是單峯的,當且僅當存在一個位置 k 使得 ai < ai+1(i < k) 且 ai > ai+1(i ≥ k)。 如今小X 想知道,對於 1 到 n 的全部排列,其中有多少個是單峯數列。 Input 第1行包含1個整數 n。 Output 第一行包含一個整數,表示答案除以 1e9 + 7 的餘數。 Examplenode
unimodal.in | unimodal.out |
2 | 2 |
Scoring • 對於 20% 的數據, n ≤ 10。 • 對於 50% 的數據, n ≤ 105。 • 對於 100% 的數據, 2 ≤ n ≤ 1018。
ios
解析:c++
1.1 20 分作法git
生成全部全排列並判斷,時間複雜度 O(n · n!)。
1.2 50 分作法
根據排列組合能夠發現,峯頂必定是 n,所以考慮 1 ∼ n - 1 分別放在 n 的左邊仍是右邊,一一得出相應
的惟一方案。因此答案就是 2^(n-1),時間複雜度 O(n)。
1.3 100 分作法
對2^(n-1)用快速冪便可,時間複雜度 O(log n)。
代碼:數組
#include<bits/stdc++.h> using namespace std; template <class T> inline void readl(T &x) { x=0;bool f=0;char ch=getchar(); while(!isdigit(ch)) { f=(ch==45);ch=getchar();} while( isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x=f?(~x+1):x;} long long n; const int md=1e9+7; inline long long bpow(long long a,long long b) { long long base=a; long long ans=1; while(b) { if(b&1) ans=(ans%md*base%md)%md; base=base*base%md; b>>=1; } return ans%md; } int main() { freopen("unimodal.in","r",stdin); freopen("unimodal.out","w",stdout); readl(n); printf("%lld\n",(bpow(2,n-1)%md)); return 0; }
小X 感到很無聊,從櫃子裏翻出了他小時候玩的積木, 這套積木共有 n 塊,每塊積木都是1個長方體。小X 想用這些積木拼成1個積木塔(沒必要每塊 積木都使用, 所謂積木塔,就是將積木1個1個摞起來,(除去最底層的積木外)每塊積木的底下必須能被它下面 的積木的底面徹底包含(即對應的長寬都要更小或相等)。固然,積木能夠任意放置,便可以以任意一面 做爲底面。 如今小X 想知道,積木塔最大能拼多高。 Input 第⼀⾏包含⼀個整數 n。 接下來 n ⾏,每⾏包含三個整數 a; b; c,表⽰該塊積木是⼀個 a × b × c 的長⽅體。 Output 第⼀⾏包含⼀個整數,表⽰答案。 Example優化
brick.in | brick.out |
3 spa 8 7 6設計 3 9 4指針 1 10 5orm |
18 |
Explanation 選擇第 1 塊積木和第 3 塊積木。
Scoring • 對於 10% 的數據, n = 1。
• 對於 40% 的數據, n ≤ 6。
• 對於 100% 的數據, 1 ≤ n ≤ 15, 1 ≤ a; b; c ≤ 108。
解析:
2.1 10 分作法
輸出 maxfa; b; cg。
2.2 40 分作法
生成全排列,而後枚舉每一個積木哪一個面朝上,時間複雜度 O(n! · 3n)。
2.3 100 分作法
顯然是狀態壓縮 DP。設計狀態 f[S][i][0/1/2] 表示已經用了集合 S 內的積木,最頂上是編
號爲 i 的積木,它的哪一個面朝上。轉移時枚舉不在 S 內的積木,以及朝上的面判斷便可。時間
複雜度 O(2n · (3n)2)。
PS:可是這道題的數據有點弱,只要搜索寫得好,照樣能夠過。
代碼:
1.DP
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long using namespace std; int readl() { int x=0,fg=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fg=-fg;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*fg; } int n,a[200],b[200],c[200],t[20],mx=-100,bin[200]; ll ans=-100; ll f[(1<<15)+10][16][3]; int main() { freopen("brick.in","r",stdin); freopen("brick.out","w",stdout); n=readl(); for(int i=1;i<=n;i++) { a[i]=readl();b[i]=readl();c[i]=readl(); t[1]=a[i];t[2]=b[i];t[3]=c[i]; sort(t+1,t+4); a[i]=t[1];b[i]=t[2];c[i]=t[3]; mx=max(mx,t[3]); } if(n==1){printf("%d",mx);return 0;} bin[1]=1; for(int i=2;i<=20;i++)bin[i]=bin[i-1]*2; for(int i=1;i<=n;i++) { f[bin[i]][i][0]=a[i]; f[bin[i]][i][1]=b[i]; f[bin[i]][i][2]=c[i]; } //printf("%lld %lld %lld\n",f[1][1][0],f[1][1][1],f[1][1][2]); for(int x=0;x<(1<<n);x++) for(int i=1;i<=n;i++) { if(x&bin[i])continue; for(int j=1;j<=n;j++) { if((x&bin[j])==0)continue; int s=x|bin[i]; if(b[j]>=b[i]&&c[j]>=c[i])f[s][i][0]=max(f[x][j][0]+a[i],f[s][i][0]); if(c[j]>=c[i]&&a[j]>=b[i])f[s][i][0]=max(f[s][i][0],f[x][j][1]+a[i]); if(b[j]>=c[i]&&a[j]>=b[i])f[s][i][0]=max(f[s][i][0],f[x][j][2]+a[i]); if(c[j]>=c[i]&&b[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][0]+b[i]); if(c[j]>=c[i]&&a[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][1]+b[i]); if(b[j]>=c[i]&&a[j]>=a[i])f[s][i][1]=max(f[s][i][1],f[x][j][2]+b[i]); if(c[j]>=b[i]&&b[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][0]+c[i]); if(c[j]>=b[i]&&a[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][1]+c[i]); if(b[j]>=b[i]&&a[j]>=a[i])f[s][i][2]=max(f[s][i][2],f[x][j][2]+c[i]); ans=max(ans,max(f[s][i][0],max(f[s][i][1],f[s][i][2]))); //printf("%d %d %d %d ***\n",x,s,i,j); //printf("%lld %lld %lld\n",f[s][i][0],f[s][i][1],f[s][i][2]); } } printf("%lld",ans); fclose(stdin);fclose(stdout); return 0; }
2.DFS
#include<bits/stdc++.h> using namespace std; template <class T> inline void readl(T &x) { x=0;bool f=0;char ch=getchar(); while(!isdigit(ch)) { f=(ch==45);ch=getchar();} while( isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x=f?(~x+1):x;} int n,maxn=0; bool vis[1001]; struct node { int mx,h[4],x[4],y[4];}e[100]; void dfs(int H,int x,int y) { maxn=max(maxn,H); for(register int i=n;i>=1;i--) if(!vis[i]) { vis[i]=1; for(register int j=1;j<=3;j++) if(e[i].x[j]<=x&&e[i].y[j]<=y) dfs(H+e[i].h[j],e[i].x[j],e[i].y[j]); vis[i]=0; } } int main() { freopen("brick.in","r",stdin); freopen("brick.out","w",stdout); readl(n); for(register int i=1,x,y,z;i<=n;i++) { readl(x);readl(y);readl(z); e[i].mx=max(x,max(y,z)); e[i].x[1]=min(x,y);e[i].y[1]=max(x,y);e[i].h[1]=z; e[i].x[2]=min(y,z);e[i].y[2]=max(y,z);e[i].h[2]=x; e[i].x[3]=min(x,z);e[i].y[3]=max(x,z);e[i].h[3]=y; } if(n==1){printf("%d",e[1].mx);return 0;} for(int i=1;i<=n;i++) { vis[i]=1; for(int j=1;j<=3;j++) dfs(e[i].h[j],e[i].x[j],e[i].y[j]); vis[i]=0; } printf("%d",maxn); return 0; }
小X 望着草稿紙上的數列,結合本身對同餘的粗淺認識,又想到了個新問題。 對於1個長度爲 n 的數列 {ai},每次詢問將給出1組數 l; r; p; q,小X 想知道有多少個 i 知足 l ≤ i ≤ r 且 ai ≡ q (mod p)。 小X 沉迷這個問題,所以一共進行了 m 次詢問。 Input 第⼀⾏包含兩個整數 n; m。 第⼆⾏包含 n 個整數,表⽰數列 faig。 接下來 m ⾏,每⾏包含四個整數 l; r; p; q,表⽰⼀次詢問。 Output m ⾏,每⾏包含⼀個整數,表⽰該次詢問的答案。 Example
congruence.in | congruence.out |
5 2 1 5 2 3 7 1 3 2 1 2 5 3 0 |
2 1 |
Scoring • 對於 20% 的數據, ai ≤ 1。
• 對於 60% 的數據, ai ≤ 100。
• 對於 100% 的數據, 1 ≤ m ≤ 105, 1 ≤ l ≤ r ≤ n ≤ 105, 0 ≤ q < p ≤ 10000, 0 ≤ ai ≤ 10000。
解析:
3.1 20 分作法
ai 6 1,所以轉變爲求區間內 0 和 1 的個數,用前綴和優化,注意 p = 1 的狀況。時間複雜
度 O(n + m)。
3.2 60 分作法
同上面,用至多 101 個前綴和便可,時間複雜度 O(ai(n + m))。
3.3 100 分作法
觀察到,其實要求的是某一範圍內 kp + q 的個數,當 p 較大時, k 的取值範圍很小。不妨
設「較大」的界限是 > K。
考慮將問題拆開來並排序,這樣每一個問題就變成了詢問 1 ∼ r 中有多少個 kp + q。維護⼀
個哈希數組, h[i] 表示 i 有多少個;以及⼀個模數數組 g[i][j],表示模 i 爲 j 有多少個。
每次指針向右移,直到移動到當前詢問的位置,每移一次就將這個數分別在兩個數組內標
記,複雜度整體是 O (nK)。
每次詢問時,對於較小的 p 直接在 g 中查詢,對於較⼤的 p 枚舉 k 並在 h 中查詢。
能夠看出 K = √ai = 100 時最優。
代碼:
1.100分
#include<bits/stdc++.h> using namespace std; template <class T> inline void readl(T &x) { x=0;bool f=0;char ch=getchar(); while(!isdigit(ch)) { f=(ch==45);ch=getchar();} while( isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x=f?(~x+1):x;}//讀入優化 int n,m; int a[100100],sum[100100],ans[100100]; struct range { int r,id,p,q,opt;}e[100100<<1];//注意開兩倍的空間 int maxn=0; int cmp(range a,range b) { return a.r<b.r;} int main() { freopen("congruence.in","r",stdin); freopen("congruence.out","w",stdout); readl(n);readl(m); for(int i=1;i<=n;i++) readl(a[i]); for(int i=1,l,r,p,q;i<=m;i++) { readl(l);readl(r);readl(p);readl(q); e[i].r=l-1; e[i].id=i; e[i].p=p; e[i].q=q; e[i].opt=-1;//須要計算sum[r]-sum[l-1] e[i+m].r=r;e[i+m].id=i;e[i+m].p=p;e[i+m].q=q;e[i+m].opt=1;//將左右兩邊分開計算,將無序的提問變爲線性的區間計算 } sort(e+1,e+1+2*m,cmp); int tail=0;//解析中的指針 for(int i=1;i<=2*m;i++) { while(tail<e[i].r) { tail++; sum[a[tail]]++;//將全部的數字出現的個數都統計一遍 maxn=max(maxn,a[tail]);//統計最大值,方便肯定下文kp+q的界限 } for(int j=0;j*e[i].p+e[i].q<=maxn;j++)//解析中的kp+q ans[e[i].id]+=sum[j*e[i].p+e[i].q]*e[i].opt;//e[i].id表示詢問的序號,若是opt=-1就表明左界限,乘積爲負表明減去[1,l-1]的值,opt=1同理 } for(int i=1;i<=m;i++) printf("%d\n",ans[i]);//輸出答案 return 0; }
2.60分(重點是理解40%的前綴和寫法)
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long using namespace std; int readl() { int x=0,fg=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fg=-fg;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*fg; } int n,m,a[101000],s[101000],v[100010][101]; int ac[101000]; int calc(int l,int r,int p,int q) { int ret=0; for(int i=l;i<=r;i++) if(a[i]%p==q%p)ret++; return ret; } int main() { freopen("congruence.in","r",stdin); freopen("congruence.out","w",stdout); n=readl();m=readl(); int bo=0; for(int i=1;i<=n;i++) { a[i]=readl();if(a[i]>1)bo=1; } int ans,l,r,p,q; if(n<=5010&&m<=5010) { for(int i=1;i<=m;i++) { l=readl();r=readl();p=readl();q=readl(); printf("%d\n",calc(l,r,p,q)); } return 0; } if(bo==0) { for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i]; for(int i=1;i<=m;i++) { l=readl();r=readl();p=readl();q=readl(); if(p==1){ans=r-l+1;printf("%d\n",ans);continue;} if(q%p==0)ans=r-l+1-s[r]+s[l-1]; if(q%p==1)ans=s[r]-s[l-1]; if(q%p>1)ans=0; printf("%d\n",ans); } return 0; } for(int i=1;i<=n;i++)//重點 for(int j=0;j<=100;j++) { v[i][j]=v[i-1][j]; if(a[i]==j)v[i][j]++; } for(int i=1;i<=m;i++) { l=readl();r=readl();p=readl();q=readl();ans=0; if(p==1){ans=r-l+1;printf("%d\n",ans);continue;} ac[0]=0; for(int i=0;i<=100;i++) if(i%p==q%p)ac[++ac[0]]=i; for(int i=1;i<=ac[0];i++) ans=ans+v[r][ac[i]]-v[l-1][ac[i]]; printf("%d\n",ans); } fclose(stdin);fclose(stdout); return 0; }