11.14 解题报告
T1 装箱
ZAY 决定将收获的 \(N\) 个奖杯分装进一些箱子内。在 ZAY 的房间中,奖杯排列在输送带上,依次编号为 \(1…N\) 。奖杯 \(i(1\le i \le N)\) 的大小为 \(A_i\) 由于分拣不方便,同一个箱子内,奖杯的编号必须连续。一个箱子内最多可以装 \(M\) 个奖杯。在一个箱子内装一些奖杯的成本为 \(K+s\times (a-b)\)。 \(K\) 是箱子本身的成本,所有箱子的成本一样。 \(s\) 是该箱子中奖杯的数目。\(a\) 是该箱子中最大奖杯的大小, \(b\) 是该箱子中最小奖杯的大小。
求包装这 \(N\) 个奖杯所需的最小成本。
思路:
直接 \(O(nm)\) DP。一眼题。
int n, m, k;
int A[20004];
int f[20004];
signed main(){
memset(f, 0x3f, sizeof(f));
n=read(); m=read(); k=read();
for(int i=1; i<=n; ++i) A[i]=read();
f[0]=0;
for(int i=1; i<=n; ++i) {
int maxx=A[i], minn=A[i];
for(int j=i; j>=max(i-m+1, 1LL); --j) {
maxx=max(maxx, A[j]); minn=min(minn, A[j]);
f[i]=min(f[i], f[j-1]+k+(i-j+1)*(maxx-minn));
}
}
cout<<f[n];
}
T2 二进制
ZAY 喜欢看《具体数学》,有一天,他在上面看到一个这样的问题:每个数都可以写成 \(\sum^k_{i=1}(-1)^{q_i}*2^{k_i}\),现在要你求出最小的 \(i\)。ZAY 显然会做,但是他还是要问你怎么做。
一行一个 \(n\) 表示长度,
第二行为该数的二进制表示。
思路:
单个 \(1\) 的贡献就是 \(1\) ,连续的 \(1\) 可以表示为 \(2^i-2^j\) ,贡献为 \(2\)。
两个连续的 \(1\) 中间隔着 \(1\) 个 \(0\) ,可以看成先把后面那个进位,然后和前面那一个再产生 \(2\) 的贡献。
如此,一直进位,剩下单个 \(1\) 再产生 \(1\) 的贡献。
int n;
char c[1000006];
ll ans;
int main(){
n=read();
scanf("%s", c);
int js=0;
for(int i=n-1; i>=0; --i) {
if(c[i]=='1') js++;
else {
if(js>1) ans++, js=1;
else if(js==1) ans++, js=0;
}
}
ans+=min(js, 2);
cout<<ans;
}
T3 意念合一
一个长度为 \(n\) 的大数,给一些限制条件。
第 \(l1\) 到 \(r1\) 和第 \(l2\) 到 \(r2\) 相同,问合法的总方案数。
30分暴力并查集。
int far[100005];
int find(int x) {
return far[x]==x?x:far[x]=find(far[x]);
}
int n, m;
struct node {
int l1, l2, r1, r2;
}sz[100005];
bool cmp(node a, node b) {
return a.l1<b.l1;
}
ll ans;
int main(){
n=read(); m=read(); ans=n;
for(int i=1; i<=n; ++i) far[i]=i;
for(int i=1; i<=m; ++i) {
sz[i].l1=read(); sz[i].r1=read(); sz[i].l2=read(); sz[i].r2=read();
for(int j=sz[i].l1, k=sz[i].l2; j<=sz[i].r1; ++j, ++k) {
int fx=find(j), fy=find(k);
if(fx==fy) continue ;
far[fx]=fy; ans--;
}
}
cout<<Qpow(10, ans-1)*9%MOD;
}
T4 飞跃计划
给 \(2^L\) 个数从 \(0\) 到 \(2^L-1\)。每个数有一个 \(0\) 到 \(9\)。
给定 \(Q\) 个串,有 \(0,1,?\) 。\(?\) 表示可 \(0\) 可 \(1\)。
问所有合法的数的权值和。
思路:
对于每一个串,\(01\) 集合和 \(?\) 集合分开,然后对 \(?\) 集合求子集,拼到 \(01\) 集合上,然后权值和就行了。
加一点技巧就能 \(100\)。
int L, N, Q;
int sz[2000006];
char c[30];
int kkk=-1;
int main(){
L=read(); Q=read();
N=(1<<L)-1;
for(int i=0; i<=N; ++i) scanf("%1d", &sz[i]);
while(Q--) {
scanf("%s", c);
int sum1=0, sum2=0;
int js=0;
for(int i=0; i<L; ++i) {
sum1<<=1; sum2<<=1;
if(c[i]!='?') sum1+=c[i]-'0';
else sum2++, ++js;
}
int ans=0;
if(js==L) {
if(kkk==-1) {
for(int i=sum2;;i=((i-1)&sum2)) {ans+=sz[i|sum1];if(i==0) break;}
kkk=ans;
}
else ans=kkk;
}
else for(int i=sum2;;i=((i-1)&sum2)) {ans+=sz[i|sum1];if(i==0) break;}
cout<<ans; putchar('\n');
}
}