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还不行,得用数学方法推公式优化,极其恶心
细细体会
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#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); }
jzoj5330. 【NOIP2017提高A组模拟8.22】密码 (难题)
比上一道题更加毒瘤
基本思路是,首先高精度除法将n转换为p进制数
枚举状态中,要尝试枚举竖式加法式子下端的数
然后可以计算出竖式最上端的数的范围,同数字取值范围取交集,然后统计答案。。。。
然而不能枚举竖式式子下面的数,因为枚举时间会爆炸。。。。。
然后发现每当下面数+1,一个限定区间会右移一格,于是等差数列求和。。。还要分段计算,根据程序中nv的取值$0,-1,p-1,p$分4种情况讨论。。。。
然后还要滚动数组优化
有毒。。。。。。。。。
细细体会
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#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); }
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]$即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#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 总结咕咕咕
以后再做