7.7

7.7

积木

Description

⼩XX 感到很⽆聊,从柜⼦⾥翻出了⾃⼰⼩时候玩的积⽊。

这套积⽊⼀共有nn 块,每块积⽊都是⼀个长⽅体。⼩XX 想⽤这些积⽊拼成⼀个积⽊塔(不必每⼀块积⽊都使⽤)。

所谓积⽊塔,就是将积⽊⼀个⼀个摞起来,(除去最底层的积⽊外)每块积⽊的底⾯必须能被它下⾯的积⽊的底⾯完全包含(即对应的长宽都要更⼩或相等)。当然,积⽊可以任意放置,即可以以任意⼀⾯作为底⾯。

现在⼩XX 想知道,积⽊塔最⼤能拼多⾼。

对于10%的数据,n=1。

对于40%的数据,n≤6。

对于100%的数据,1≤n≤15,1≤a,b,c≤10^8。

Solution

啊这道题其实暴力dfs就能过,正解是状压(想到了,没写,想法很接近了,但。。。划水了当时)

f[ 状态 ] [第i个] [0/1/2枚举三个面]

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
int read()
{
    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<<16)][16][3];
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		t[1]=read();t[2]=read();t[3]=read();
		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;
	}
	for(int i=1;i<=n;i++){
		f[1<<(i-1)][i][0]=a[i];
		f[1<<(i-1)][i][1]=b[i];
		f[1<<(i-1)][i][2]=c[i];
	}
	for(int x=0;x<(1<<n);x++)
		for(int i=1;i<=n;i++){
			if(x&(1<<(i-1)))continue;
			for(int j=1;j<=n;j++){
				if(x&(1<<(j-1))==0)continue;
				int s=x|(1<<(i-1));
				if(b[j]>=b[i]&&c[j]>=c[i])f[s][i][0]=max(f[s][i][0],f[x][j][0]+a[i]);
				if(a[j]>=b[i]&&c[j]>=c[i])f[s][i][0]=max(f[s][i][0],f[x][j][1]+a[i]);
				if(a[j]>=b[i]&&b[j]>=c[i])f[s][i][0]=max(f[s][i][0],f[x][j][2]+a[i]);
				
				if(b[j]>=a[i]&&c[j]>=c[i])f[s][i][1]=max(f[s][i][1],f[x][j][0]+b[i]);
				if(a[j]>=a[i]&&c[j]>=c[i])f[s][i][1]=max(f[s][i][1],f[x][j][1]+b[i]);
				if(a[j]>=a[i]&&b[j]>=c[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("%lld",ans);
	return 0;
}

暴力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;
    }
 

同余

Description

小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 ⾏,每⾏包含⼀个整数,表⽰该次询问的答案。

• 对于 20% 的数据, ai ≤ 1。

  • 对于 60% 的数据, ai ≤ 100。

   • 对于 100% 的数据, 1 ≤ m ≤ 105, 1 ≤ l ≤ r ≤ n ≤ 105, 0 ≤ q < p ≤ 10000, 0 ≤ ai ≤ 10000。

Solution

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 时最优。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N = 100005; 
int n,m,a[N],ans[N],mx,sum[N];
struct node{
	int x,q,p,op,id;
}e[N<<1];
bool cmp(node a,node b){
	return a.x<b.x;
} 
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1,l,r,q,p;i<=m;i++){
		l=read();r=read();p=read();q=read();
		e[i].x=l-1;e[i].op=-1;e[i].p=p;e[i].q=q;e[i].id=i;
		e[i+m].x=r;e[i+m].op=1;e[i+m].p=p;e[i+m].q=q;e[i+m].id=i;
	}
	sort(e+1,e+1+2*m,cmp);
	int t=0;
	for(int i=1;i<=2*m;i++){
		while(t<e[i].x){
			t++;
			sum[a[t]]++;//统计个数
			mx=max(mx,a[t]);
		}
		for(int j=0;j*e[i].p+e[i].q<=mx;j++)
			ans[e[i].id]+=e[i].op*(sum[j*e[i].p+e[i].q]);
	}
	for(int i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
} 
posted @ 2020-08-14 00:12  ke_xin  阅读(515)  评论(0编辑  收藏  举报