8.1特别行动
40分 rank 30 加油!
T1:概率期望DP+倒退套路
T2:思维
T3:暴力枚举优化(预处理+ST表+二分确定最优区间)
T4:有限背包优化+根号拆分+无限背包转化
T1:给你n类武器,每次可以买一个武器(买到每类武器的概率是1/n),第k次购买期望钱数是k,求买完所有武器的期望钱数
如果按照常规简单思路,单独考虑每个武器买到的期望钱数*n是不对的,个人认为是每个武器购买不是相互独立的?
发现对于购买到j件武器可以从无穷状态转移过来,但是从还差j件武器回收到还差j+1件武器,可以只考虑本次购买是否增加贡献
f[i]买到i件武器的期望步数=1+f[i-1](i)/n+f[i](1-i/n)为什么不对呢?因为我要求钱数,如果正就无法达到面积转换的效果,也就求不出来g(钱数DP)
1
1 2
1 2 3
这是g倒着累加的过程,形象理解一下
f[i]买到i件武器,最终离达到n件武器的状态还差的期望步数:这样倒着推f[n]=0,就完全可以转移了
郭学长口谕:一半概率期望就线性性一下,不对再DP就好
给你0--n-1坐标系,每次投骰子,骰到多少就往后走多少步,如果a-->b有父亲关系,那么到a必须立刻到b,问跳过(刚好)终点n的期望步数:
f[i]:代表到坐标系i位置的期望步数,我也倒着来f[i]:从i超过终点的期望步数,如果fa[i]=i,那么转移有意义,所以从6个状态转移累加(可能是倒着真的又好想又方便输出答案,所以记住他吧)
ans today exam:
double n;
double f[N],g[N];
int main()
{
scanf("%lf",&n);
f[(int)n]=g[(int)n]=0.0;
f_(i,n-1,0)
{
// f[i]=1+(i/n)*(f[i])+(1-i/n)*(f[i+1]);
f[i]=(n)/(n-i)+f[i+1];
}
f_(i,n-1,0)
{
//g[i]=1+(i/n)*(g[i]+f[i])+(1-i/n)*(g[i+1]+f[i+1]);
g[i]=n/(n-i)+((double)i/(n-i))*f[i]+g[i+1]+f[i+1];
}
//无穷-->有穷
chu("%.2lf",g[0]);
return 0;
}
ans another
https://blog.csdn.net/qkoqhh/article/details/79451489
T2:给你一颗树,每个根节点的val是它的儿子节点的val的最值,0代表最小,1代表最大,现在假设有r个叶子,给每个叶子附上值,组成r的排列,问rt的最大权值
rt权值越大,那么对val的要求就越高(似乎要是每个节点权值都无穷大那么一定存在很优秀的方案),可二分性,假设最大权值是k,那么max:它的子树一定要有一颗里面的最大值出现k,min:所有儿子的赋予权值必须都>=k,f[u]:表示如果说u的权值要>=x,至少需要多少个叶子的值>=x,如果max:只要一个,如果min,所有
推广到u的儿子不是叶子,会发现这就是一个贪心向上传递的结果累加,在每个节点我都选择最优的一种赋值方案,使得nowrt节点的“下界”更靠上,那么最终一定是最优解
至于考虑x的假设值可能不一样,为什么可以向上传递,其实通过这个想到思路,完全可以抛弃它,就是我每次选择一种赋值方法(不影响上面结果),使得局部最优,然后传上去就行
const int N=1000000+100;ll mod=998244353;
int n,op[300000+10],fa[300000+10],dp[300000+10];
bool isn[300000+10],ok[300000+10];
int main()
{
//freopen("shuju.in","r",stdin);
// freopen("dfs.txt","w",stdout);
n=re();int ki=0;
_f(i,1,n)op[i]=re();
_f(i,2,n)fa[i]=re(),isn[fa[i]]=1;//不是儿子
_f(i,1,n)if(!isn[i])dp[i]=1,++ki;
f_(i,n,1)
{
if(op[fa[i]])
{
if(ok[fa[i]])dp[fa[i]]=min(dp[fa[i]],dp[i]);
else dp[fa[i]]=dp[i],ok[fa[i]]=1;
}
else
{
dp[fa[i]]+=dp[i];
}
}
//_f(i,1,n)chu("dp[%d]:%d\n",i,dp[i]);
chu("%d",ki-dp[1]+1);
return 0;
}
/*
dp[rt]=cnt:rt节点要>=某个值,至少的叶子节点需要>=这个值的数量
如果rt取max,那么只要有一个就行
dp[rt]=min(dp[son])
如果rt取min,必须所有叶子节点都要>=这个值
dp[rt]=sigma(dp[son])
如果满足fa[i]<i的性质当然要BFS!
T3:给你由1234组成的矩阵,规定只有
12
34或
1122
1122
3344
3344
以此类推的正方形是合法的形态,然后多次询问,给出一个矩阵区间(a,b)(c,d)问你合法正方形的最长面积
(暴力)首先想到问题的一些可简化性质,对于每个右上角(a,b)都可以唯一对应一个合法【】,所以我可以预处理出所有(a,b)是左上角的【】的长度,然后每次询问暴力扫每个点看合法不合法
另10% 的数据,粉色的格子很少,那么合法的矩形也很少,所以可以预处理出来每个合法的矩形,回答询问时
去扫每一个矩形,如果在给定矩形内则更新答案
(正解)还是预处理f[i][j][k]:是(a,b)是左上角,k是每一个块长度是否可行,处理前缀和,接下来的答案,考虑二分!!!如果已知在一个区间(a,b)--(c,d)的长度lenth的【】存在,那么1lenth-1的一定存在,所以当发现合法区间内lenth存在1lenth-1一定存在,就可以尝试lenth+1(同理的,elnth存在,lenth+1可能存在),只要保证在合法区间内就行
[经典:对于f[][][]的处理方式,在矩阵中的DP,先搞单个的g[i][j][k]表示右下角(i,j)种类是k的【】最长长度]
DP的部分
_f(i,1,n)
{
scanf("%s",sas+1);
_f(j,1,m)
{
if(sas[j]=='B')s[i][j]=1;
else if(sas[j]=='W')s[i][j]=2;
else if(sas[j]=='P')s[i][j]=3;
else s[i][j]=4;
}
}
_f(i,1,n)
{
_f(j,1,m)
{
g[i][j][s[i][j]]=min(g[i-1][j][s[i][j]],min(g[i-1][j-1][s[i][j]],g[i][j-1][s[i][j]]))+1;//是上面3个格子里最短的+1
//chu("g[%d[%d(%d):%d\n",i,j,s[i][j],g[i][j][s[i][j]]);
}
}
_f(i,1,n)
{
_f(j,1,m)
{
int lentha=(min(n-i+1,m-j+1))>>1;//最长单个
_f(k,1,lentha)
{
bool p1=(g[i+k-1][j+k-1][1]>=k);
bool p2=(g[i+2*k-1][j+k-1][3]==k);
bool p3=(g[i+2*k-1][j+2*k-1][4]==k);
bool p4=(g[i+k-1][j+2*k-1][2]==k);
if(p1&&p2&&p3&&p4)f[i][j][k]=1;
//chu("can(f[%d][%d][%d]:%d\n)\n",i,j,k,f[i][j][k]);
}
}
}
//f[i][j][k]:(i,j)左上长度2*k是否可行
//then f[i][j][k],从(i,j)-->(n,m)长度是否可行
f_(i,n,1)
{
f_(j,n,1)
{
int lentha=(min(n-i+1,m-i+1))>>1;
_f(k,1,lentha)
{
f[i][j][k]=f[i+1][j][k]+f[i][j+1][k]-f[i+1][j+1][k]+f[i][j][k];
// chu("sum(f[%d][%d][%d]:%d\n)\n",i,j,k,f[i][j][k]);
}
}
}
int n,m;
char sas[510];
int s[510][510];
int f[510][510][260],g[510][510][5],a,b,c,d;
inline int Sum(int a1,int b1,int a2,int b2,int jk)
{
if(a1>a2||b1>b2)return 0;
return f[a1][b1][jk]+f[a2+1][b2+1][jk]-f[a2+1][b1][jk]-f[a1][b2+1][jk];
}
inline bool check(int le)
{
if(!le)return true;
// chu("(%d)(%d,%d)-->(%d,%d)can?\n",le,a,b,c-le*2+1,d-le*2+1);
if(Sum(a,b,c-le*2+1,d-le*2+1,le)>0)return true;
// chu("NOT\n");
return false;
}
int main()
{
// freopen("exam.txt","r",stdin);
n=re(),m=re();
int qeu=re();
_f(i,1,n)
{
scanf("%s",sas+1);
_f(j,1,m)
{
if(sas[j]=='B')s[i][j]=1;
else if(sas[j]=='W')s[i][j]=2;
else if(sas[j]=='P')s[i][j]=3;
else s[i][j]=4;
}
}
_f(i,1,n)
{
_f(j,1,m)
{
g[i][j][s[i][j]]=min(g[i-1][j][s[i][j]],min(g[i-1][j-1][s[i][j]],g[i][j-1][s[i][j]]))+1;
//chu("g[%d[%d(%d):%d\n",i,j,s[i][j],g[i][j][s[i][j]]);
}
}
_f(i,1,n)
{
_f(j,1,m)
{
int lentha=(min(n-i+1,m-j+1))>>1;//最长单个
_f(k,1,lentha)
{
bool p1=(g[i+k-1][j+k-1][1]>=k);
bool p2=(g[i+2*k-1][j+k-1][3]==k);
bool p3=(g[i+2*k-1][j+2*k-1][4]==k);
bool p4=(g[i+k-1][j+2*k-1][2]==k);
if(p1&&p2&&p3&&p4)f[i][j][k]=1;
//chu("can(f[%d][%d][%d]:%d\n)\n",i,j,k,f[i][j][k]);
}
}
}
//f[i][j][k]:(i,j)左上长度2*k是否可行
//then f[i][j][k],从(i,j)-->(n,m)长度是否可行
f_(i,n,1)
{
f_(j,n,1)
{
int lentha=(min(n-i+1,m-i+1))>>1;
_f(k,1,lentha)
{
f[i][j][k]=f[i+1][j][k]+f[i][j+1][k]-f[i+1][j+1][k]+f[i][j][k];
// chu("sum(f[%d][%d][%d]:%d\n)\n",i,j,k,f[i][j][k]);
}
}
}
//然后就是二分
_f(i,1,qeu)
{
a=re(),b=re(),c=re(),d=re();
int lentha=min(c-a+1,d-b+1);//还是二分每一个小块块的长度
if(lentha&1)
{
lentha=(lentha+1)>>1;
}
else lentha>>=1;
int l=0,r=lentha;int nas=0;
// chu("lenth:%d\n",r);
while (l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
{
// chu("(%d,%d)(%d):can\n",a,b,mid);
l=mid+1;nas=mid;
// chu("nas:%d\n",nas);
}
else r=mid-1;
}
// chu("nas:%d\n",nas);
nas<<=1;
chu("%d\n",nas*nas);
}
return 0;
}
T4:给你n件物品,第i件物品体积是i有i个,问装满n体积的背包方案数
首先sqrt(n)以内的可以用n^2算法算过,f[i][j]表示前i件物品填满体积j的背包方案数,f[i][j]=f[i-1][j]+f[i][j-i],对于v>=i(i+1)的体积,可能会有超过i个i物品参与,所以v要减去不合法f[i-1][j-i(i+1)],f[i][j]:假如j一旦超过了ii的最多数量,那么我就立刻把不合法的f[i-1][j-i(i+1)]的方案数减去,这时候f[i][j]就算j>i*(i+1),那么它最多只有i个背包参与,再取转移下一个状态f[i][j+i],那么也最多超限一个,那么我j+i也会在继承f[i][j]的基础上减去那可能多超限的1个,所以不用考虑超限2/3/4....
然后sqrt以后的每个物品最多只能选不超过sqrt(n)个,也就是不可能超限,所以我定义g[i][j]是选了i个sqrt(n)+1 ~ n中的物品,体积刚好是j的方案数,问题可以看成从数值是sqrt(n+1)~n中的数字选若干个刚好凑成j的方案数量,我考虑一步一步从初始0状态构造,对于凑成j的数f[i][j],我可以直接从选了i种j-i转移来,f[i][j-i](也就是选的物品整体右移)也可以是再多选一个最小的m+1 f[i-1][j-m-1]可以保证不重不漏
int f[N],s[N],n,g[500][N];
int main()
{
//freopen("qiandao4.in","r",stdin);
// freopen("dfs.txt","w",stdout);
n=re();
int m=sqrt(n);
f[0]=1;
_f(i,1,m)
{
_f(j,0,i)s[j]=f[j];//只从上一个继承
_f(j,i,n)s[j]=(f[j]+s[j-i])%MOD;//这一个物品也可以随便选
_f(j,0,n)
{
f[j]=s[j];
if(j-(i+1)*i>=0)f[j]=(f[j]-s[j-i*(i+1)]+MOD)%MOD;
}
}
g[0][0]=1;
ll ans=f[n];//只从小的里面选全部
_f(i,1,m)
{
_f(j,0,n)
{
if(j>=m+1&&j>=i)g[i][j]=(g[i-1][j-m-1]+g[i][j-i])%MOD;
ans=(ans+(ll)g[i][j]*f[n-j]%MOD)%MOD;
//if(j==0)chu("%d\n",g[i][0]);
}
}
chu("%lld",ans);
return 0;
}