暑 假 队 测 Round #4
\(25+25+50=100pts\).
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)\times2^{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\n",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;
}