前言
再创悲剧系列
洛谷 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[n−1][k][0](第n−1行分开,没有多出一块)
- 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[n−1][k−2][0](第n−1行分开,和第n行各不同)
- 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[n−1][k−2][1](第n−1行不分开,和第n行各不同)
- 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[n−1][k−1][0]∗2+f[n−1][k−1][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[n−1][k][1](同一块)+f[n−1][k][0]∗2(两种情况)+f[n−1][k−1][0](多出一块且前一行分开)+f[n−1][k−1][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