TEST2018.4.21(Noip2015提高组Day1)
为2019级lizw0520zzh的学生献上福利!!!
T1 跳石头(第一道二分):
看到最小的最大值就知道是二分了。。。
对这个最大值进行二分,每次判断是否只需要移走小于M块石头就行了。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN=50000+233; int a[MAXN]; int L,N,M; bool check(int d) { int ll=M,position=0; for(int i=1;i<=N+1;i++) { if(a[i]-a[position]<d) ll--; else position=i; } return ll>=0?true:false; } int main() { freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); scanf("%d%d%d",&L,&N,&M); for(int i=1;i<=N;i++) scanf("%d",a+i); a[0]=0;a[N+1]=L; int left=0,right=L+10; while(right-left>1) { int mid=left+right>>1; if(check(mid)) left=mid; else right=mid; } printf("%d\n",left); }
发现Noip2015特别喜欢二分
T2 字串:
一道很恶心的dp
一开始是这样想的:dp[i][j][k]代表A串位置到了i,B串到了j,已经用了k个子串。
那么就有:能匹配时,方案数为:单独使用当前字符为一个子串+与前面相连形成一个子串。
结果程序写出来后自己手出了一组样例发现错了???!于是就发现了如果不使用当前字符,情况是什么样的呢?这种情况就被完美忽略了不是吗?
于是就有了下面这种神奇的dp:
设s[i][j][k]为A用到了i,B用到了j,已经用了k个子串, 并且一定用了当前字符(A[i])时的方案数。
设f[i][j][k]为A用到了i,B用到了j,已经用了k个子串, 无论用不用当前字符(A[i])时的方案数总和。
然后“显而易见”的,可以得到如下转移方程:
s[i][j][k]=f[i-1][j-1][k-1]+s[i-1][j-1][k] 和 f[i][j][k]=f[i-1][j][k]+s[i][j][k]
但是一看这个空间限制,一脸黑人问号了。。才128MB,玩什么?
于是真正考试的时候我这道题是错了的qwq
等到听评讲的时候才想到第一维可以写成滚动数组
然后就有如下代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define mod 1000000007 using namespace std; int a,b,k; char A[1002],B[1002]; int f[3][1000][1000],s[3][1000][1000]; int main() { freopen("substring.in","r",stdin); freopen("substring.out","w",stdout); scanf("%d%d%d\n",&a,&b,&k); scanf("%s",A+1); scanf("%s",B+1); int now=1,past=0; f[0][0][0]=1; for(int e=1;e<=a;e++) { f[now][0][0]=1; for(int j=1;j<=b;j++){ for(int t=1;t<=k;t++){ if(A[e]==B[j])s[now][j][t]=(s[past][j-1][t]+f[past][j-1][t-1])%mod; else s[now][j][t]=0; f[now][j][t]=(f[past][j][t]+s[now][j][t])%mod; } } swap(now,past); } cout<<f[past][b][k]; return 0; }
但是!!!我并没有这样写!!!
我的代码有点难理解,而且不如上面那个代码简单,但是只用开一个数组,长这样:(大家可以看注释)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int Mod=1000000000+7; const int N=1000+23; const int M=200+23; char cha[N],chb[M]; int f[2][N][M]; int n,m,k,u,o,p,ans; int main() { freopen("substring.in","r",stdin); freopen("substring.out","w",stdout); scanf("%d%d%d",&n,&m,&k); scanf("%s%s",cha,chb); for(int i=n;i>=1;i--) cha[i]=cha[i-1]; //预处理字符串 for(int i=m;i>=1;i--) chb[i]=chb[i-1]; //预处理字符串 for(int i=1;i<=n;i++) if(cha[i]==chb[1]) f[0][i][1]=1; //预处理相同开头的字符串 for(int l=2;l<=m;l++) { //dp过程 u=(o+1)%2; for(int j=1;j<=k;j++) { p=0; for(int i=1;i<=n;i++) { p=(p+f[o][i-1][j-1])%Mod; if(cha[i]==chb[l]) f[u][i][j]=(f[o][i-1][j]+p)%Mod; //更新方案数 else f[u][i][j]=0; //如果这两个位置上的字符不同,就没有方案 } } o=u; } for(int i=1;i<=n;i++) ans=(ans+f[o][i][k])%Mod; //累加方案数 printf("%d\n",ans); return 0; }
是不是很难懂?反正我想了好久。。。
T3 运输计划(第二道二分):
是道难题,反正考试的时候打个暴力拿了15分(非洲脸,明明理论60的,可能有小细节出错了吧)。
2015真的特别喜欢二分啊,而我不太擅长的就是二分了(还好我生的晚),这道题的正解是:
lca+二分+树上倍增(为了提速)+差分
详细的大家可以看这位大佬的博客,只是他的代码太丑了(他应该不会看到这篇博客的。。),所以我就贴自己的啦~~
大佬博客: ouql的博客
Tarjan求lca是个好东西(手动滑稽)
代码:
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> using namespace std; const int N=300000+233; struct Node { int too,pre,dis; }e[N<<1],lik[N<<1]; int n,m,x,y,z,l,r,mid,mx; int val[N],fa[N]; int tot,tot2; int q[N][4],cntt[N],cnt[N]; int last1[N],last2[N],d[N]; bool v[N]; void add(int x,int y,int z) { e[++tot].too=y; e[tot].dis=z; e[tot].pre=last1[x]; last1[x]=tot; } void add2(int x,int y,int z) { lik[++tot2].too=y; lik[tot2].dis=z; lik[tot2].pre=last2[x]; last2[x]=tot2; } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void tarjan(int x) { v[x]=1;fa[x]=x; for(int i=last2[x];i;i=lik[i].pre) if(v[lik[i].too]) q[lik[i].dis][3]=find(lik[i].too); for(int i=last1[x],too=e[i].too;i;i=e[i].pre,too=e[i].too) if(!v[too]) { d[too]=d[x]+e[i].dis; tarjan(too); fa[too]=x; } } int dfs(int x,int fa) { int sum=cnt[x]; for(int i=last1[x],too=e[i].too;i;i=e[i].pre,too=e[i].too) if(too!=fa) { val[too]=e[i].dis; sum+=dfs(too,x); } return cntt[x]=sum; } bool ck() { int total=0,mxx=0; memset(cnt,0,sizeof(cnt)); for(int i=1;i<=m;i++) if(d[q[i][1]]+d[q[i][2]]-2*d[q[i][3]]>mid) { total++; cnt[q[i][1]]++; cnt[q[i][2]]++; cnt[q[i][3]]-=2; } dfs(1,0); for(int i=1;i<=n;i++) if(cntt[i]==total&&val[i]>mxx) mxx=val[i]; return mx-mxx<=mid; } int main() { freopen("transport.in","r",stdin); freopen("transport.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n-1;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add2(x,y,i);add2(y,x,i); q[i][1]=x;q[i][2]=y; } tarjan(1); for(int i=1;i<=m;i++) mx=max(mx,d[q[i][1]]+d[q[i][2]]-2*d[q[i][3]]); l=0;r=mx; while(l<r) { mid=(l+r)/2; if(ck()) r=mid; else l=mid+1; } printf("%d\n",l); }最后照例祝大家早日AK(反正我是这辈子都不可能AK了)!