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;
}
posted on 2022-08-01 21:34  HZOI-曹蓉  阅读(12)  评论(0编辑  收藏  举报