小X歸來 模擬賽1 解析

 Problem1 單峯

 

 X 歸來後,首先對數列很感興趣。他想起有1類特殊的數列叫單峯數列。 咱們說一個數列 {ai} 是單峯的,當且僅當存在一個位置 k 使得 ai < ai+1(i < k) ai > ai+1(i k)。 如今小X 想知道,對於 1 n 的全部排列,其中有多少個是單峯數列。 Input 第1行包含1個整數 nOutput 第一行包含一個整數,表示答案除以 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;
	}

 

Problem2 積木

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 151 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;
	}

 

Problem3 同餘

小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 1051 l r n 1050 q < p 100000 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;
}
		
相關文章
相關標籤/搜索