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

posted @ 2022-11-14 22:07  Konnya_ku  阅读(49)  评论(0编辑  收藏  举报