TEST2018.5.5(Noip2015提高组Day2)

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了)!

猜你喜欢

转载自blog.csdn.net/y554280697/article/details/80283397