做的中等题,难题总结【6~7月】

jzoj4612. 【NOIP2016模拟7.12】游戏(中等题)

将每一个空格看作边,与每一个划分出来的长条连边作二分图匹配,注意软石头也要被划分不然长条编号不对

jzoj4224. 【五校联考3day1】食物(中等题)

注意到预算和美味度都 $\leq 50000$,于是作状态互换dp,多重背包要用单调队列优化

jzoj4631. 【GDOI2017模拟7.15】背单词(中等题)

可以将所有字符串倒插进树中,手玩一下就发现子树中的标号连续时最优,然后可以证明先向大小更小的子树走答案最优,排序dfs即可

jzoj3052. 【NOIP2012模拟10.25】剪草(中等题)

我们发现先剪生长速度慢的草更优(可以通过模拟2个草使用2种不同方法剪在2段连续时间的情况证明),又可以得到一棵草不会被连续剪2次。那么只需判断每个草是否被剪,dp即可。

比如有一棵草x,生长速度为b,高度为a,另一棵草y,生长速度为d,高度为c(假设b>d)

那么先剪y再剪x,最后长度为d,先剪x再剪y,长度为b,显然先剪y再剪x最优

jzo4804. 【NOIP2016提高A组模拟9.28】成绩调研 (中等题)

我原来的想法是正确的,后来too naive把它毙了。。。。。 这种题目就2种做法,序列分治和尺取法,正确做法是尺取法

维护2个上界r1和r2,r1代表“对于i这个左端点,使所有数的个数都大于等于题目要求下界的最左的左端点”,r2代表"对于i这个左端点,使所有数的个数都小于等于题目要求上界的最右的右端点“

于是左端点i对应答案就是r2-r1+1,累加即可

jzoj4628. 【NOIP2016提高A组模拟7.15】立方体(中等题)

需要写另一个生成器生成24种方块摆放方式,然后根据这24种摆放方式生成转移进行bfs。。。有点恶心

jzoj3295. 【SDOI2013】泉 (中等题)

以前做过类似的一道题jzoj4377,但是这道题更加难,原来的字符串编号+桶法不行了

并且空间卡的很严。。。。。。不可以使用hash。。。。。。只能排序。。。。

事实上,我们可以枚举每一组数据,然后2^6爆枚数据相同的位,将每组数据的这些位提取出来,排序根据乘法原理统计答案即可

jzoj4684. 【GDOI2017模拟8.11】卡牌游戏(中等题)

直接排序维护显然不行

事实上可以权值线段树,对于每一个节点维护值l(d同学的牌个数),r(p同学牌的个数),c(答案)

对于每个节点拿右儿子的l消掉左儿子的r,统计c就好了

jzoj3170.【GDOI2013模拟4】挑选玩具(中等题)

这题题解给了一种玄学的做法,我并不会

事实上,这题可以高维前缀和。设f[i]表示玩具集合包含于i集合的箱子个数,然后用f[全集]容斥一下即可

jzoj4806. 【NOIP2016提高A组五校联考3】打工(中等题)

这题我本来想的斯特林数,因为20%部分分的转移很像斯特林数,但是就一直没想纯dp。。。。

事实上,这题可以dp,将斯特林数的公式变形一下即可,设$f[i][j][k]$表示前i个人,分配到了j队,是否达到了字典序上限$

所有有上界的dp几乎都可以设"是否达到了字典序上限"状态

显然有一个性质,第i个数的编号只能≤历史编号最大值+1

然后转移即可(注意,j要枚举到i,因为分的队个数可能大于题目要求的序列中队的个数!!!!)

 

jzoj4633. 【GDOI2017模拟7.15】萌萌哒(难题)

挺有趣的一题

听说有分块做法,但是被卡常了

这题一般区间数据结构很难做,考虑倍增,维护$log_2 n$个并查集,设$f[j][i]$表示从i后,长度为$2^j$的子串,对应与谁相同

倍增是正确的,因为并查集基本操作满足结合律

每次可以将$l1,r1$ $l2,r2$拆成log个长度为2的幂次的区间,然后将左边的区间和右边的区间合并,放在对应的f里

看上去这样是结束了,但是我们要求的是最底层$f[0]$的信息,所以要下传信息

事实上,可以枚举$j,i$来下传,$f[j-1][i]$应该与$f[j][i]$合并,$f[j-1][i+(1<<(j-1))]$应该与$f[j][i]+(1<<(j-1))$合并

注意到$f[j][i]$可能对应多个区间,但是这些区间的值都是相等的,所以$f[j-1][i]$可以任取与$f[j][i]$相等的区间合并,$f[j][i]$是这些区间的代表,符合条件,$f[j-1][i+(1<<(j-1))]$的合并同理

最后使用乘法原理统计答案即可

jzoj6170. 【GDSOI 2019 day2】高中生数学题 (中等题)

套上一个kummer定理进行dp

设$f[i][j][k][l]$表示构造了i个数,要进j次位,下一位是否必须进位,是否达到上限的答案

毒瘤,我原来想错了3次,暴力dp还不行,得用数学方法推公式优化,极其恶心

细细体会

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,p,kk,f[100][100][2][2],ans,a[110];
int ct;
int main(){
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    scanf("%lld%lld%lld",&n,&p,&kk);
    if(p==1){
        printf("%lld",n);
        return 0;
    }
    f[0][0][0][1]=1;
    while(n){
        a[++ct]=n%p;
        n/=p;
    }
    reverse(a+1,a+ct+1);
    for(int i=0;i<ct;i++)
        for(int j=0;j<ct;j++)
            for(int k=0;k<2;k++)
                for(int l=0;l<2;l++)if(f[i][j][k][l]){
                    ll lm=p-1;
                     if(l)lm=a[i+1];
                        for(int nj=0;nj<2;nj++){
                            ll c=a[i+1]-p-nj+p*k+1,d=a[i+1]-nj+p*k;
                            ll nl=max(c,0ll),nr=min(lm,d);
                            if(nr!=lm)
                                f[i+1][j+nj][nj][0]+=f[i][j][k][l]*(nr-nl+1ll);
                            else{
                                f[i+1][j+nj][nj][0]+=f[i][j][k][l]*(nr-nl);
                                f[i+1][j+nj][nj][l]+=f[i][j][k][l];
                            }
                        }
                }
    for(int i=kk;i<=ct;i++)
        ans+=f[ct][i][0][0]+f[ct][i][0][1];
    printf("%lld",ans);
}
View Code

 

jzoj5330. 【NOIP2017提高A组模拟8.22】密码 (难题)

比上一道题更加毒瘤

基本思路是,首先高精度除法将n转换为p进制数

枚举状态中,要尝试枚举竖式加法式子下端的数

然后可以计算出竖式最上端的数的范围,同数字取值范围取交集,然后统计答案。。。。

然而不能枚举竖式式子下面的数,因为枚举时间会爆炸。。。。。

然后发现每当下面数+1,一个限定区间会右移一格,于是等差数列求和。。。还要分段计算,根据程序中nv的取值$0,-1,p-1,p$分4种情况讨论。。。。

然后还要滚动数组优化

有毒。。。。。。。。。

细细体会

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mo 1000000007ll
ll p,kk,f[2][4010][2][2],ans,a[4010],d[4010],t[100010];
char c[100010];
int ct;
ll mod(){
    ll ans=0;
    for(int i=d[0];i>=1;i--)
        ans=(ans*10+d[i])%p;
    return ans;
}
ll s(ll x){
    return (x*(x+1ll)/2ll)%mo;
}
ll ss(ll x,ll y){
    return (s(y)-s(x-1ll)+mo)%mo;
}
ll div(){
    ll ans=0,ok=0,ct=0;
    for(int i=d[0];i>=1;i--){
        ans=(ans*10+d[i]);
        if(!ok&&ans>=p){
            t[++ct]=ans/p;
            ans%=p;
            ok=1;
        }
        else if(!ok&&ans<p)continue;
        else if(ok){
            t[++ct]=ans/p;
            ans%=p;
        }
    }
    reverse(t+1,t+ct+1);
    memcpy(d,t,sizeof(d));
    d[0]=ct;
    return ans;
}
int main(){
    freopen("password.in","r",stdin);
    freopen("password.out","w",stdout);
    scanf("%s",c);
    scanf("%lld%lld",&p,&kk);
    int l=strlen(c);
    for(int i=0;i<l;i++)
        d[i+1]=c[i]-'0';
    reverse(d+1,d+l+1);
    d[0]=l;
    while(d[0]){
        a[++ct]=mod();
        div();
    }
    reverse(a+1,a+ct+1);
    int ps=0,ns=1;
    f[ns][0][0][1]=1;
    for(int i=0;i<ct;i++){
        memset(f[ps],0,sizeof(f[ps]));
        for(int j=0;j<ct;j++)
            for(int k=0;k<2;k++)
                for(int l=0;l<2;l++)if(f[ns][j][k][l]){
                    ll lm=p-1;
                     if(l)lm=a[i+1];
                        for(int nj=0;nj<2;nj++){
                            int nv=p*k-nj;
                            if(nv==0){
                                f[ps][j+nj][nj][0]=(f[ps][j+nj][nj][0]+(f[ns][j][k][l]*((ss(1,lm))%mo)%mo))%mo;
                                f[ps][j+nj][nj][l]=(f[ps][j+nj][nj][l]+f[ns][j][k][l]*(lm+1ll)%mo)%mo;
                            }
                            if(nv==-1){
                                f[ps][j+nj][nj][0]=(f[ps][j+nj][nj][0]+(f[ns][j][k][l]*((ss(1,lm-1))%mo)%mo))%mo;
                                f[ps][j+nj][nj][l]=(f[ps][j+nj][nj][l]+f[ns][j][k][l]*(lm)%mo)%mo;
                            }
                            if(nv==p-1){
                                f[ps][j+nj][nj][0]=(f[ps][j+nj][nj][0]+(f[ns][j][k][l]*(ss(p-lm+1ll,p))%mo)%mo)%mo;
                                f[ps][j+nj][nj][l]=(f[ps][j+nj][nj][l]+f[ns][j][k][l]*(p-lm)%mo)%mo;
                            }
                            if(nv==p){
                                f[ps][j+nj][nj][0]=(f[ps][j+nj][nj][0]+(f[ns][j][k][l]*(((ss(p-lm,p-1ll)))%mo)%mo))%mo;
                                f[ps][j+nj][nj][l]=(f[ps][j+nj][nj][l]+f[ns][j][k][l]*(p-lm-1ll)%mo)%mo;
                            }
                        }
                }
        swap(ps,ns);
    }
    for(int i=kk;i<=ct;i++)
        (ans+=f[ns][i][0][0]+f[ns][i][0][1])%=mo;
    printf("%lld",ans);
}
View Code

 

jzoj3172. 【GDOI2013模拟4】贴瓷砖(中等题)

可以从左到右扫描一遍字符串,在ac机上作匹配,记$ls[i]$表示在原字符串中,所有与区间在后缀$[1,i]$匹配,且匹配结尾点恰好为i的模板串的长度最大值,然后可以从右到左扫描一遍ls[i],求出另外一个数组$g[i]$表示$[i,n]$区间最左边的被瓷砖覆盖的点,然后扫描$g[i]$,当$i<g[i]$时即可更新答案

jzoj4616. 【NOI2016模拟7.12】二进制的世界 (中等题)

这题我本来想的高维前缀和。。。。但是不可以这样做。。。。

注意到这个问题可以转化成:

有一个集合$s$,初始为空,你需要支持以下操作

1.加入一个数

2.给出$x$,询问 $x$与集合中每一个数进行opt运算,答案的最大值

这道题有2种暴力

第一种暴力:用一个桶$b$,$b[i]$表示第$i$个数是否出现,每次查询,扫一遍这个桶统计答案

第二种暴力:用一个桶$c$,$c[i]$表示权值为$i$的数与集合内所有的数运算所得答案的最大值,每次查询只要查$c[val]$即可

发现两种暴力都有优势和劣势,所以考虑平衡规划

但是如果定期重构,有多个数的情况不可一次在可以承受的运算量内计算,于是只能考虑值平衡

设$f[i][j]$表示出现在$s$中的前8位为i的数,后8位与$j$运算的最大值

那么每次加入一个数,可以把这个数拆成前8位的$a$和后8位的$b$,然后对于所有j,用$b opt j$更新$f[a][j]$

每次查询时,可以把查询数拆成前8位的$a$和后8位的$b$,然后枚举所有的$i$,用$f[i][b]+opt(i,a)$更新答案

方案数同理

jzoj3171. 【GDOI2013模拟4】重心 (难题)

设$f[i]$表示i上面所有方块(包括i)的重心$x$,与i上面所有方块(包括i)的右边界的距离

可以注意到,无论如何平移这些方块,$f$值不变

显然可以平移上面的方块使上面方块重心横坐标为0,然后统计答案,答案就是$f[2]$

考虑如何计算$f[i]$,如果之前所有方块的质量为$M$,重心坐标为$x$,当前方块的质量为m,以第i个方块的底边为x轴,左下角为原点建立平面直角坐标系

显然当前方块的几何重心为$1$,x可以在$[0,2]$这个区间随意取

根据定义,新的重心横坐标为$\frac{M*x+m}{M+m}$

新的方块右边界为$max(2,x+f[i-1])$

则$f[i]=max(2,x+f[i-1]) - \frac{M*x+m}{M+m}$

$= max(\frac{m+2*M-M*x}{M+x},\frac{m*x-m+f[i-1]*M+f[i-1]*m}{M+m})$

容易发现左边的这个式子的值随x的增大而减小,右边这个式子的值随x的增大而增大

所以当x取到${0,2}$时,答案最优

用这个值更新$f[i]$即可

#include<bits/stdc++.h>
using namespace std;
int n;
double f[300010],s,ans,a[300010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf",&a[i]);
    f[n]=1;s+=a[n];
    for(int i=n-1;i>1;i--){
        f[i]=max(((double)2*s+a[i])/(s+a[i]),(a[i]+f[i+1]*(s+a[i]))/(s+a[i]));
        s+=a[i];
    }
    printf("%.6lf",f[2]);
}
代码

 

jzoj3232. 【佛山市选2013】排列 (中等题)

这道题我还要看题解才做的出来。。。瞎了

事实上,我们可以枚举一个质因子,以及其的次数,然后再设$f[i][j]$表示枚举到第i个质因子,和为j的最大答案

然而结果太大,可以将每一个数取对数来处理,注意对数要重复利用

 

还有noip 某次模拟赛的诡异分块c题(大毒瘤),一道结论题(不知道如何证明),gdsoi d1t1 总结咕咕咕

以后再做

转载于:https://www.cnblogs.com/rilisoft/p/10945388.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值