暑 假 队 测 Round #4

(25+25+50=100pts).

T1:Folding
T2:数字串
T3:炸毁城市

T1:区间dp

非常经典的区间dp题。

  • 如果不能压缩:(f[l][r]=f[l][k]+f[k+1][r])
  • 如果能压缩:(f[l][r]=cnt+2+f[l][k]) (cnt指压缩后的长度)

求解过程中需要判断两个字符串是否相等,hash可以O(1)查询。
但这题数据范围较小,暴力比较即可。

Code:

#include<bits/stdc++.h>
#define ll long long
#define inf 30
using namespace std;
const ll N=150;
const ll mod=19260817;
const ll b=161;
ll f[N][N],sum[N],p[N];
char st[N];
int check(int l,int m,int r){
	int len2=r-l+1,len1=m-l+1;
	if(len2%len1)return inf;
	int x=len2/len1;
	for(int i=1;i<=x;i++){
		int tt=l+(i-1)*len1;
		for (int j=0;j<len1;j++)
			if(st[l+j-1]!=st[tt+j-1])return inf;
	}
	return x;
}
int main(){
	p[0]=1;
	scanf("%s",st);
	ll n=strlen(st);
//	for(ll i=1;i<n;i++){
//		sum[i]=(sum[i-1]*b%mod+(st[i]-'A'+1)%mod)%mod;
//		p[i]=p[i-1]*b%mod;
//	}
	memset(f,0x3f,sizeof(f));
	for(ll i=1;i<=n;i++)f[i][i]=1;
	for(ll len=2;len<=n;len++)
		for(ll l=1;l<=n-len+1;l++){
			ll r=l+len-1;
			f[l][r]=min(f[l][r],len);
			for(ll k=l;k<r;k++){
				f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
				ll bk=check(l,k,r);
				if(bk==inf)continue;
				ll num=bk,cnt=0;
				while(num>0)num/=10,cnt++;
				f[l][r]=min(f[l][r],cnt+2+f[l][k]);
			}	
		}
	printf("%lld",f[1][n]);
	return 0;
}

T2:我也不知道怎么做

听这题时在划水。。代码也没写出来。
这里口胡一下部分分做法吧。

(10pts): 怎么暴力怎么来。
(25) ~ (40pts):依次拆分每个数,前缀和统计,O(1)回答查询,注意细节能够拿到40甚至更多,像我写的这么烂只有25。
(70pts):40分做法+压位。
(100pts):
这题正解细节较多的,稍不小心就会沦为和暴力老哥同分。
n位二进制所有1的个数是可以直接求的。
(2^{n-1}+(n-1) imes2^{n-2})
可以用二分快速求出求得长度。
我们知道第一位一定是1 如(100,1000,10000)
我们统计1,然后删除前导0,这样就又回到原来的问题,所以可以用递归求解。

T3:tree dp

第一个回答是个很简单的treedp。
战略游戏一模一样。
不懂的可以去看下qzwer大佬的Blog
而第二问难度就加强了很多。
(f[i][1])为炸第i个结点对整棵树来说最少需要炸多少个结点,(f[i][0])则不炸.
当一个结点在任何最优情况下都不可能被炸掉时,显然:

  • (f[i][1]>f[i][0])

一个很直观的想法就是以每个结点为根,做一次与第一问同样的treedp。
每次进行判断,如果对结果有贡献,就做上标记,最后输出没有标记的节点。
时间复杂度(O(n^2)),只能拿到50分,机房有大佬通过玄学优化艹到了80分。
正解是换根+二次扫描。讲解会比较啰嗦。直接上代码le.

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,tot,f[N][2],g[N][2],r[N][2];
int pre[N],now[N],to[N],sm[N],sg[N];
void add(int x,int y){
	pre[++tot]=now[x];
	to[tot]=y;now[x]=tot;
}
void dfs(int u,int fa){
	f[u][1]=1,f[u][0]=0;
	for(int i=now[u];i;i=pre[i]){
		int v=to[i];
		if(v==fa)continue;
		dfs(v,u);
		f[u][1]+=min(f[v][1],f[v][0]);
		f[u][0]+=f[v][1];
	}
}
void dp(int u,int fa){
	for(int i=now[u];i;i=pre[i]){
		int v=to[i];
		if(v==fa)continue;
		g[v][1]=r[u][1]-min(f[v][1],f[v][0]);
		g[v][0]=r[u][0]-f[v][1];
		r[v][0]=sm[v]+g[v][1];
		r[v][1]=sg[v]+min(g[v][0],g[v][1])+1;
		dfs(v,u);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v;i<n;i++){
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}	
	dfs(1,0);
	int ans=min(f[1][0],f[1][1]);
	printf("%d
",ans);
	r[1][1]=f[1][1],r[1][0]=f[1][0];
	for(int i=1;i<=n;i++)
		for(int j=now[i];j;j=pre[j])
			sm[i]+=f[to[j]][1],sg[i]+=min(f[to[j]][1],f[to[j]][0]);
	dp(1,0);
	for(int i=1;i<=n;i++)
		if(r[i][0]<=r[i][1])printf("%d ",i);
	return 0;
}
原文地址:https://www.cnblogs.com/Xxhdjr/p/13508994.html