2018_10_17 模拟赛

99 篇文章 1 订阅
47 篇文章 0 订阅

前言

再创悲剧系列


洛谷 2700 逐个击破

题目

给出一个 n n n个点的树,问使其中的 k k k个点(给定)各自不连通需要删除的边的最小代价


分析

然而这道题说实话还是比较好理解的,用kruskal,没错,只是思想,那么需要用到并查集,那问题是具体过程,那么可以把边权从大到小排序,当两点的father值都为 k k k个点以内说明这条路是答案中的一条(越往后越小),所以代码出来了


代码

#include <cstdio>
#include <algorithm>
#define min(a,b) ((a)<(b))?(a):(b)
#define max(a,b) ((a)>(b))?(a):(b)
#define rr register
using namespace std;
typedef unsigned uit;
struct node{
    uit x,y,w;
    bool operator <(const node &t)const{
        return w>t.w;
    }
}e[100001];
inline signed iut(){
    rr uit ans=0; rr char c=getchar();
    while (c<48||c>57) c=getchar();
    while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
    return ans;
}
uit f[100001],v[100001],n=iut(); long long ans;
inline signed getf(uit u){return (f[u]==u)?u:f[u]=getf(f[u]);}
signed main(){
    for (rr uit i=1;i<=n;++i) f[i]=i;
    for (rr uit k=iut();k;--k) v[iut()+1]=1;
    for (rr uit i=1;i<n;++i) e[i]=(node){iut()+1,iut()+1,iut()};
    sort(e+1,e+n);
    for (rr uit i=1,fa,fb;i<n;++i){
        fa=getf(e[i].x),fb=getf(e[i].y);
        if (v[fa]^v[fb]) f[fa+fb-v[fa]*fa-v[fb]*fb]=v[fa]*fa+v[fb]*fb;//只有一个点是k个点以内
        else f[min(fa,fb)]=max(fa,fb),ans+=(v[fa]&&v[fb])*e[i].w;//按秩合并
    }
    return !printf("%lld",ans);
}

JZOJ 2937 监听还原

分析

这道题可以说,就是一道kmp的题目,然而比较玄学,因为是后半部分和前半部分的匹配,主要是边界没搞好,错了一次又一次,然而就尴尬了


代码

#include <cstdio>
#define rr register
using namespace std;
char s[100001],tne[26]; int len,fail[100001];
signed main(){
	for (rr int i=0;i<26;++i) tne[getchar()-97]=i+97;
	rr char c=getchar();
	while (c<97||c>122) c=getchar();
	while (c>96&&c<123) s[++len]=c,c=getchar();
	for (rr int i=(len+1>>1)+1,j=0;i<=len;++i){//注意边界
        while (j&&s[i]!=tne[s[j+1]-97]) j=fail[j];
        fail[i]=(j+=(s[i]==tne[s[j+1]-97]));
    }
    for (rr int i=1;i<=len-fail[len];++i) putchar(s[i]);
    for (rr int i=1;i<=len-fail[len];++i) putchar(tne[s[i]-97]);//两段“相同”的
    return 0;
}

JZOJ 2938 分割田地

分析

然而这道题果然是dp,而且还是比较坑的,设 f [ n ] [ k ] [ 0 / 1 ] f[n][k][0/1] f[n][k][0/1]表示前1到 n n n行,连通块数为 k k k,且该行分开/不分开
那么

  • f [ n ] [ k ] [ 0 ] + = f [ n − 1 ] [ k ] [ 0 ] ( 第 n − 1 行 分 开 , 没 有 多 出 一 块 ) f[n][k][0]+=f[n-1][k][0](第n-1行分开,没有多出一块) f[n][k][0]+=f[n1][k][0](n1)
  • f [ n ] [ k ] [ 0 ] + = f [ n − 1 ] [ k − 2 ] [ 0 ] ( 第 n − 1 行 分 开 , 和 第 n 行 各 不 同 ) f[n][k][0]+=f[n-1][k-2][0](第n-1行分开,和第n行各不同) f[n][k][0]+=f[n1][k2][0](n1n)
  • f [ n ] [ k ] [ 0 ] + = f [ n − 1 ] [ k − 2 ] [ 1 ] ( 第 n − 1 行 不 分 开 , 和 第 n 行 各 不 同 ) f[n][k][0]+=f[n-1][k-2][1](第n-1行不分开,和第n行各不同) f[n][k][0]+=f[n1][k2][1](n1n)
  • f [ n ] [ k ] [ 0 ] + = f [ n − 1 ] [ k − 1 ] [ 0 ] ∗ 2 + f [ n − 1 ] [ k − 1 ] [ 1 ] ∗ 2 ( 只 多 出 一 块 , 所 以 如 果 合 并 可 能 会 有 两 种 情 况 ) f[n][k][0]+=f[n-1][k-1][0]*2+f[n-1][k-1][1]*2(只多出一块,所以如果合并可能会有两种情况) f[n][k][0]+=f[n1][k1][0]2+f[n1][k1][1]2()
  • f [ n ] [ k ] [ 1 ] = f [ n − 1 ] [ k ] [ 1 ] ( 同 一 块 ) + f [ n − 1 ] [ k ] [ 0 ] ∗ 2 ( 两 种 情 况 ) + f [ n − 1 ] [ k − 1 ] [ 0 ] ( 多 出 一 块 且 前 一 行 分 开 ) + f [ n − 1 ] [ k − 1 ] [ 1 ] ( 多 出 一 块 且 前 一 行 不 分 开 ) f[n][k][1]=f[n-1][k][1](同一块)+f[n-1][k][0]*2(两种情况)+f[n-1][k-1][0](多出一块且前一行分开)+f[n-1][k-1][1](多出一块且前一行不分开) f[n][k][1]=f[n1][k][1]()+f[n1][k][0]2()+f[n1][k1][0]()+f[n1][k1][1]()

代码

#include <cstdio>
#define rr register
#define min(a,b) (((a)<(b))?(a):(b))
using namespace std;
const int mod=100000007;
int f[2][2001][2],n,k;
signed main(){
	scanf("%d%d",&n,&k);
	f[1][1][1]=f[1][2][0]=1;
	for (rr int i=2;i<=n;++i)
	    for (rr int j=1;j<=min(i<<1,k);++j)
	    	f[i&1][j][0]=(f[!(i&1)][j][0]+((j>2)?f[!(i&1)][j-2][0]+f[!(i&1)][j-2][1]:0)+((j>1)?(f[!(i&1)][j-1][0]+f[!(i&1)][j-1][1]<<1):0))%mod,
		    f[i&1][j][1]=(f[!(i&1)][j][1]+f[!(i&1)][j][0]*2+((j>1)?f[!(i&1)][j-1][0]+f[!(i&1)][j-1][1]:0))%mod;
	printf("%d",(f[n&1][k][0]+f[n&1][k][1])%mod);
	return 0;
}

后续

sto ssl_all of dalaos

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值