题解
讲道理,其实从算法的思路上来说,实现15年day2的题,在代码量上并没什么难度..但确实还是比较考验思维含量的orz。而我….日常沙雕。
第一题——跳石头(stone)
【题目描述】
- 给出距离L,在0-L之间有n个点。先在删去m个点使得点之间(包括0和L)的最小距离差最大。
- ok,这是道一眼就知道的二分题。二分出一个答案,线性扫描区间内的点位置。
- 如果两点之间的差值大于这个答案,就保留后面一个点。统计出保留的点的个数和题目所需要保留的点的个数。(传统二分)
- 然而首测只有70分。因为二分打的不熟练所以有三个点的最优解直接被略过了。感谢巨佬excitedfrog提醒。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
}
const int MAXN=50100;
int L,n,m,minn;
int a[MAXN];
int check(int x){
int sum=0;
int temp_pos=0;
for (int i=1;i<=n;i++){
if(a[i]-temp_pos>=x){
sum++;
temp_pos=a[i];
}
}
if(L-temp_pos<x) sum--;
return sum;
}
int main(){
fff();
minn=999999999;
scanf("%d%d%d",&L,&n,&m);
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
minn=min(minn,a[i]-a[i-1]);
}
a[n+1]=L;
if(n==m){
printf("%d",L);
return 0;
}
int l=minn-1,r=L,mid;
while (l<=r){
mid=(l+r)/2;
int tmp=check(mid);
if(tmp>n-m){
l=mid+1;
}else if(tmp<n-m){
r=mid-1;
}else{
int temp_pos=0,ans=999999999;
for (int i=1;i<=n;i++){
if(a[i]-temp_pos>=mid){
ans=min(ans,a[i]-temp_pos);
temp_pos=a[i];
}
}
ans=min(ans,L-temp_pos);
printf("%d",ans);
return 0;
}
}
int temp_pos=0,ans=999999999;
for (int i=1;i<=n;i++){
if(a[i]-temp_pos>=r){
ans=min(ans,a[i]-temp_pos);
temp_pos=a[i];
}
}
ans=min(ans,L-temp_pos);
printf("%d",ans);
}
第二题——子串(substring)
【题目描述】
- 给出字符串a,和b,要求在串a中求出k段相连与b匹配。求出所有匹配的方案个数对1000000007取模。
- 其实这道题对思维含量还是比较考验的。对于内存的限制也是比较高。刚开始没打滚动数组,只测了70分。
- 讲一下方程 f[i][j][k] f [ i ] [ j ] [ k ] 表示A段i字符与B段j字符匹配时取k段的方案数。那么就可以从 f[i−1][j−1][k] f [ i − 1 ] [ j − 1 ] [ k ] 和 f[i之前和j−1位匹配的位置][j−1][k] f [ i 之 前 和 j − 1 位 匹 配 的 位 置 ] [ j − 1 ] [ k ] 的和。
- 这里前置和就可以用 sum[j][k] s u m [ j ] [ k ] 来解决, jk j k 的意义和上面一样。
- 但这个复杂度看着数据就知道肯定会爆。那么观察方程发现i只会从i-1转移过来。那么将j,k倒序循环,就可以省略i位的空间。
- 代码真的特别短orz
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long
#define MOD 1000000007
using namespace std;
void fff(){
freopen("substring.in","r",stdin);
freopen("substring.out","w",stdout);
}
int n,m,K;
char A[1110],B[220];
int dp[220][220]={1},sum[220][220];
int nxt[1100];
int main(){
fff();
scanf("%d%d%d",&n,&m,&K);
memset(nxt,-1,sizeof(nxt));
scanf("%s",A);
scanf("%s",B);
for (int i=1;i<=n;i++){
for (int j=m;j>=1;j--){
for (int k=1;k<=K;k++){
if(A[i-1]==B[j-1]) sum[j][k]=(dp[j-1][k-1]+sum[j-1][k])%MOD;
else sum[j][k]=0;
dp[j][k]=(dp[j][k]+sum[j][k])%MOD;
}
}
}
cout<<dp[m][K];
}
第三题——运输计划(transport)
【题目描述】
- 给出n个点的树,具有边权,并给出m对点,求去掉某一边权之后的所有对点上的路径权值最大值最小。
又是最大值最小的题。首选还是二分。虽然我首测的时候实在是想不出来,看着数据打了把m=1的情况骗走二十分。但还是不是很满意orz。因为有40分的数据是一条链的没有打出来orz
首先知道这个是一棵树,两点之间的距离就可以用到LCA算法来解决。边权就下沉到子节点上。
- 再来解决去掉一条边上的问题。求出所有路径上的总和之后,根据二分答案进行判断和树上差分(这个自己百度吧….),记录下有多少对点需要进行减去权值的操作的。并且求出这些路径的最大值。
- 如果一个点覆盖的点的个数刚好是需要减去操作点的个数,那就是需要判断这些点当中权值最大的。如果路径的最大值减去这个最大的权值小于二分的答案,那就是合法的情况。
- 注意快读邻接表卡常。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#define LL long long
#define MAXN 300010
#include <queue>
using namespace std;
void fff(){
freopen("transport.in","r",stdin);
freopen("transport.out","w",stdout);
}
int n,m,tot=0;
struct Edge{
int to,nxt,w;
}edge[MAXN<<1];
int head[MAXN];
int readn(){
char c=getchar();
int x=0;
while (c<'0'||c>'9') c=getchar();
while (c=='-'||(c>='0'&&c<='9')) x=x*10+c-'0',c=getchar();
return x;
}
void add(int from,int to,int w){
edge[++tot].nxt=head[from];
edge[tot].w=w;
edge[tot].to=to;
head[from]=tot;
}
struct query{
int f,t,lca,cost;
}qu[MAXN];
bool visited[MAXN];
int depth[MAXN],fa[MAXN][20],dist[MAXN],dd[MAXN];
queue <int> q;
void bfs(){
memset(visited,false,sizeof(visited));
visited[1]=true;
q.push(1);
depth[1]=1;
while (!q.empty()){
int u=q.front();q.pop();
for (int i=head[u];i;i=edge[i].nxt){
if(!visited[edge[i].to]){
q.push(edge[i].to);
dist[edge[i].to]=dist[u]+edge[i].w;
dd[edge[i].to]=edge[i].w;
fa[edge[i].to][0]=u;
visited[edge[i].to]=true;
depth[edge[i].to]=depth[u]+1;
}
}
}
}
int Lca(int x,int y){
if(depth[x]<depth[y]){
swap(x,y);
}
for(int i=19;i>=0;i--){
if(depth[fa[x][i]]>=depth[y]){
x=fa[x][i];
}
}
if(x==y) return x;
for (int i=19;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int maxx=0;
int s[MAXN];
void dfs(int u){
visited[u]=true;
for (int i=head[u];i;i=edge[i].nxt){
if(!visited[edge[i].to]){
dfs(edge[i].to);
s[u]+=s[edge[i].to];
}
}
}
bool judge(int x){
int cnt=0,mxt=0;
memset(s,0,sizeof(s));
for (int i=1;i<=m;i++){
if(qu[i].cost<=x) continue;
cnt++;
s[qu[i].f]++;
s[qu[i].t]++;
s[qu[i].lca]-=2;
mxt=max(mxt,qu[i].cost-x);
}
memset(visited,false,sizeof(visited));
dfs(1);
int tar=0;
for (int i=1;i<=n;i++){
if(s[i]==cnt) tar=max(tar,dd[i]);
}
return mxt<=tar;
}
int main(){
fff();
n=readn(),m=readn();
for (int i=1;i<n;i++){
int f,t,w;
f=readn(),t=readn(),w=readn();
add(f,t,w);
add(t,f,w);
}
for (int i=1;i<=m;i++){
qu[i].f=readn();
qu[i].t=readn();
}
bfs();
for (int j=1;j<20;j++){
for (int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
for (int i=1;i<=m;i++){
qu[i].lca=Lca(qu[i].f,qu[i].t);
qu[i].cost=dist[qu[i].f]+dist[qu[i].t]-2*dist[qu[i].lca];
maxx=max(maxx,qu[i].cost);
}
int l=-1,r=maxx+1;
#define mid (r+l)/2
while (r-l>1){
if(judge(mid)) r=mid;
else l=mid;
}
printf("%d",r);
return 0;
}