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