3.29省选练习

 

如果现在离省选只剩最后一周,现在的我是否具有进队的能力$?$

显然是没有吧,如今省选确实推迟了,但是我又能提高到省队水平吗,貌似也不太可能

诶,还是说,我们可能现在会说,如果我早学一年,我就...

嗯$?$都到现在了,说这个确实很无力,不如把每一天都充实的度过,即使省选前最后一天,也是在进步的过程

有了这样的心态,最后的结果是什么,我都能欣然接受,即使可能会失去很多东西,胡思乱想是没有的啊,一直努力就好了$!$
want to talk

 

$T1$

设$dp[i][j]$表示我们使用了$j$张牌,组成$i$的方案数

转移比较寄,我看了半天没看懂(当我发现题解里面没写差分我特别想把这个人拉出来打一顿)...

$dp[n][k]=C_4^0f(n-k,k)+C_4^1(n-k,k-1)+C_4^2(n-k,k-2)+C_4^3(n-k,k-3)+C_4^4(n-k,k-4)$

这个就是一个类似差分的过程

我们这里就类似与枚举选第几个数字的过程了

上面的转移式子大概是$:$在上次选择数字的基础上,我们将一些数字加$1$的过程

我们枚举多少个数字是$1,$这样每次往后推$n$次的时候,就相当于选出来的最大值是$n$了

大概每次往后推都是整体加$1$的过程,然后我们选择这一步是否要新加一张$1$

现在转移比较显然了,那么考虑矩阵加速即可

感觉很像差分却又不是差分,只是把选数的过程倒过来了,最后还是枚举选哪些数,并且保证每种情况都考虑到了,这种转移思想确实很强,这种选数方式就相当于,用不同的方式累加出原数的过程

这道题弱化版是可以$FFT$的,毕竟在没看数据范围之前,我直接想到了生成函数

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define mod 1000000009
using namespace std;
int C4[10]={1,4,6,4,1};
int f[20][20];
struct Mat
{
    int a[110][110];
    Mat(){memset(a,0,sizeof(a));}
    int* operator [] (int x)
    {
          return a[x];
    }
    
}A,B,bas;
Mat operator * (Mat a,Mat b)
{
    Mat c;
    for(int k=1;k<=100;k++)
    {
        for(int i=1;i<=100;i++)
        {
            for(int j=1;j<=100;j++)
            {
                c[i][j]=(c[i][j]+1ll*a[i][k]*b[k][j])%mod;
            }
        }
    }
    return c;
}
Mat my_pow(Mat x,int b)
{
    Mat res;
    for(int i=1;i<=100;i++)
    {
        res[i][i]=1;
    }
    while(b)
    {
        if(b&1)
        {
            res=res*x;
        }
        x=x*x;
        b>>=1;
    }
    return res;
}
int n,k;
void Init()
{
    f[0][0]=1;
    for(int i=1;i<=10;i++)
    {
        for(int j=1;j<=i;j++)
        {
            for(int k=0;k<=min(j,4ll);k++)
            {
                f[i][j]+=f[i-j][j-k]*C4[k];
            }
        }
    }
}
signed main()
{
    Init();
    int cnt=0;
    for(int i=10;i>=1;i--)
    {
        for(int j=10;j>=1;j--)
        {
            A[1][++cnt]=f[i][j];
        }
    }
    for(int i=1;i<=10;i++)
    {
        for(int j=0;j<=min(i-1,4ll);j++)
        {
            bas[i*10-(i-j)+1][10-i+1]=C4[j];
        }
    }
    for(int i=11;i<=100;i++)
    {
        bas[i-10][i]=1;
    }
    while(1)
    {
        
        cin>>n>>k;
        if(n==k&&k==0) return 0;
        int ans=0;
        if(n<=10)
        {
            for(int i=1;i<=k;i++)
            {
                ans+=f[n][i];
            }
            cout<<ans<<"\n";
        }
        else
        {
            B=A*my_pow(bas,n-10);
            for(int i=1;i<=k;i++)
            {
                ans=(ans+B[1][10-i+1])%mod;
            }
            cout<<ans<<"\n";
        }
    }
}

 

$T2$

给定一些三维平面,询问每次能摧毁的包含被摧毁点的平面,找最小$z$

三维上包含一个点,显然暴力$KD-Tree$写就好了

比较直接的思想是,点找面,这样的话单次需要遍历一下树,显然不是很优

那么反过来,面找点,每个点只能匹配一个,又由于我们每次优先选$z$较小的,那么就排序

对点建树,那么对于面进行查询,优先找编号小的点然后删去,然后跑一遍匹配就好了

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 100000000
#define MAXN 100005
#define ls tr[x].lson
#define rs tr[x].rson
using namespace std;
int n,m,rt,cnt,nowk,Ans1;
int fa[MAXN],pos[MAXN],Ans[MAXN];
struct Mian 
{
    int xl,yl,xr,yr,z,id;
    friend bool operator <(const Mian &a, const Mian &b)
    {
           return a.z<b.z;
    }
}B[MAXN];
struct Poi 
{
    int x,y,id;
    friend bool operator <(const Poi &a, const Poi &b)
    {
           return nowk?a.x<b.x:a.y<b.y;
    }
}S[MAXN];
struct KDTREE
{
    int mn,id,x,y;
    int minx,miny,maxx,maxy;
    int lson,rson;
}tr[MAXN];
void pushup(int x) 
{
    tr[x].mn=tr[x].id;
    if(ls) 
    {
        tr[x].mn=min(tr[x].mn,tr[ls].mn);
        tr[x].minx=min(tr[x].minx,tr[ls].minx); 
        tr[x].miny=min(tr[x].miny,tr[ls].miny);
        tr[x].maxx=max(tr[x].maxx,tr[ls].maxx); 
        tr[x].maxy=max(tr[x].maxy,tr[ls].maxy);
    }
    if(rs) 
    {
        tr[x].mn=min(tr[x].mn,tr[rs].mn);
        tr[x].minx=min(tr[x].minx,tr[rs].minx); 
        tr[x].miny=min(tr[x].miny,tr[rs].miny);
        tr[x].maxx=max(tr[x].maxx,tr[rs].maxx); 
        tr[x].maxy=max(tr[x].maxy,tr[rs].maxy);
    }
}
void build(int &x,int f,int l,int r,int KD) 
{
    if(l>r)return;
    int mid=(l+r)>>1; 
    x=++cnt; 
    nowk=KD;
    nth_element(S+l,S+mid,S+r+1);
    pos[S[mid].id]=x; 
    fa[x]=f;
    tr[x].id=tr[x].mn = S[mid].id;
    tr[x].x=tr[x].minx=tr[x].maxx=S[mid].x;
    tr[x].y=tr[x].miny=tr[x].maxy=S[mid].y;
    build(ls,x,l,mid-1,KD^1); 
    build(rs,x,mid+1,r,KD^1);
    pushup(x);
}
void query(int x,int xl,int yl,int xr,int yr) 
{
    if(tr[x].maxx<xl||xr<tr[x].minx||tr[x].maxy<yl||yr<tr[x].miny)return;
    if(xl<=tr[x].minx&&tr[x].maxx<=xr&&yl<=tr[x].miny&&tr[x].maxy<=yr)
    {
        Ans1=min(Ans1,tr[x].mn); 
        return;
    }
    if(tr[x].id!=INF&&xl<=tr[x].x&&tr[x].x<=xr&&yl<=tr[x].y&&tr[x].y<=yr)
    {
       Ans1=min(Ans1,tr[x].id);
    }
    if(ls)query(ls,xl,yl,xr,yr);
    if(rs)query(rs,xl,yl,xr,yr);
}
void Del(int x) 
{
    tr[x].id=INF;
    while(x) 
    {
        pushup(x);
        x=fa[x];
    }
}
int main() 
{
    scanf("%d",&n);
    for(int i=1,xl,xr,yl,yr,z;i<=n;++i) 
    {
        scanf("%d%d%d%d%d",&xl,&xr,&yl,&yr,&z);
        B[i]=(Mian){xl,yl,xr,yr,z,i};
    }
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&S[i].x,&S[i].y),S[i].id=i;       
    }
    build(rt,0,1,m,0);
    sort(B+1,B+1+n);
    for(int i=1;i<=n;++i) 
    {
        Ans1=INF; 
        query(rt,B[i].xl,B[i].yl,B[i].xr,B[i].yr);
        if(Ans1!=INF) 
        {
            Ans[Ans1]=B[i].id;
            Del(pos[Ans1]);
        }
    }
    for(int i=1;i<=m;++i)printf("%d\n",Ans[i]);
    return 0;
}

 

$T3$

暴力$dp$较为显然

$dp[i][j][k]=\max(dp[i-1][j][k],dp[i-1][j-1][k]+u_i,dp[i-1][j][k-1]+p_i,dp[i-1][j-1][k-1]+(1-(1-u_i)(1-p_i)))$

目前这道题有$O(n^2logn)$保证正确的官方算法,和三天前(2022.3.26)刚刚被$hack$的不知道是精度问题还是别的问题出错的$wqs$二分

但是这道题被去年的学长拿出来造的都是$wqs$二分复杂度下的数据...

较为套路的是,如果存在个数限制,一般使用$wqs$二分消除限制

$dp[i][j]$表示前$i$个用了$j$个超级球和若干精灵球的最大期望,为了少用精灵球,我们加上耗费代价

那么我们目前最优解恰好用了$k$个,我们只需要加回去就好了,我们就是相当于二分斜率,然后找切点嘛~

好,套路的把第二维优化掉,然后就好了

机房讨论了一下被$hack$的原因,可能是存在一段斜率为$0$的函数导致二分出错,不过还是能过考试数据的,当然也有可能数据造假了...

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define MAXN 2010
using namespace std;
const double eps=1e-9;
int rd()
{
    int x=0,f=1;
    int ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,a,b,cnt1[MAXN],cnt2[MAXN];;
double p[MAXN],u[MAXN],f[2010];
bool Check(double v1,double v2)
{
    memset(f,0,sizeof(f));
    memset(cnt1,0,sizeof(cnt1));
    memset(cnt2,0,sizeof(cnt2));
    for(int i=1;i<=n;i++)
    {
        f[i]=f[i-1];
        cnt1[i]=cnt1[i-1];
        cnt2[i]=cnt2[i-1];
        if(f[i-1]+p[i]-v1-eps>f[i])
        {
            f[i]=f[i-1]+p[i]-v1;
            cnt1[i]=cnt1[i-1]+1;
            cnt2[i]=cnt2[i-1];
        }
        if(f[i-1]+u[i]-v2-eps>f[i])
        {
            f[i]=f[i-1]+u[i]-v2;
            cnt1[i]=cnt1[i-1];
            cnt2[i]=cnt2[i-1]+1;
        }
        if(f[i-1]+p[i]+u[i]-p[i]*u[i]-v1-v2-eps>f[i])
        {
            f[i]=f[i-1]+p[i]+u[i]-p[i]*u[i]-v1-v2;
            cnt1[i]=cnt1[i-1]+1;
            cnt2[i]=cnt2[i-1]+1;
        }
    }
    return cnt2[n]<=b;
}
double tl;
bool check(double md)
{
    double l=0,r=1;
    for(int i=1;i<=50;i++)
    {
        double mid=(l+r)/2;
        if(Check(md,mid)) r=mid;
        else l=mid;
    }
    tl=l;
    return cnt1[n]<=a;
}
int main()
{
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    n=rd(),a=rd(),b=rd();
    for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
    for(int i=1;i<=n;i++) scanf("%lf",&u[i]);
    double l=0,r=1;
    for(int i=1;i<=50;i++)
    {
        double mid=(l+r)/2;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%.8f\n",f[n]+l*a+tl*b);
    return 0;    
}

 

posted @ 2022-03-29 22:05  Authentic_k  阅读(32)  评论(0编辑  收藏  举报