您的位置:首页 > 其它

20171009离线赛总结

2017-10-10 15:09 295 查看

考试时的思路:

第一题

直接枚举

正着循环,倒着循环,求出每个点对应的L和R

第二题

20:32 2017/10/9

看了半天,把所有可能的区间预处理出来,dfs。

第三题

30分的话,用二进制枚举,看一条边取还是不取。

可以先把链的写了,输入的u到v的路径就变成了一个区间,这样的话,问题就简化为区间调度问题。

按照终点排序,然后贪心。

题解:

第一题:双击

  这道题还是相当水的,只要正着扫一遍,倒着扫一遍,每次只要判断一下是否属性发生了改变就可以了,只不过我对拍了两个多小时,没有拍出来,很难受。

#include<iostream>
#include<cctype>
#define M 500086
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
char A[M];
int n,m;
int L[M],R[M];
bool check(char c){return isdigit(c);}
int main(){
cin>>n>>m;
scanf("%s",A);
int x;
bool f=check(A[0]);
int pos=0;
FOR(i,0,n-1){
if(f==check(A[i]))L[i]=pos;
else {
f=check(A[i]);
pos=i;
L[i]=i;
}
}
f=check(A[n-1]);
pos=n-1;
DOR(i,n-1,0){
if(f==check(A[i]))R[i]=pos;
else {
f=check(A[i]);
pos=i;
R[i]=i;
}
}
while(m--){
scanf("%d",&x);
printf("%d %d\n",L[x],R[x]);
}
return 0;
}


第二题:取子串

  太宗对这题的处理非常好,大家可以看一下他的题解。

  这道题考察的是递推,但是我一开始想的是怎么从区间来考虑,但是由于各个区间之间有交叉,相离等情况,想着想着脑子又炸掉了。其实这道题的递推还是比较容易的(至少小C是这么认为的),我们用三个数组来存储,一个是dp,用于存储取用i的方案,一个是DP,用于存储不论是否取用i的方案,以及sum,用于存储前缀和。

  我们把问题可以这么看,如果第i个点满足条件,即可以与前面的形成一个T区间,那么就可以吧前i-m+1个点与这个区间合并成一个新的区间方案,同时把之前i-m的方案和加上来,因为每个方案都可以与这个新的区间合并。所以dp[i]=sum[i-m]+i-m+1。如果i不能满足条件,那么就可以把之前的方案加过来,相当于在每个方案后面接上了一个i。即:dp[i]=dp[i-1]。

  下面就是如何快速判断是否满足形成一个T区间了。

  这道题用的是哈希算法,以一个质数为基数(这里我们选择233,为什么呢?因为它是质数(玄学))然后我们就可以用unsigned类型的单位进行存储了(unsigned long long 或unsigned int)因为unsigned 类型可以自然溢出,不需考虑是否为质数(至于为什么不会重复,玄学)。预处理出T的哈希值,HashT=HashT*Hex+T[i] (Hex=233)。然后预处理出每一位S的哈希值。 Ha[i]=Ha[i-1]*Hex+A[i]。最后求出每一位的基数Base[i]=Base[i-1]*Hex。只需判断(Ha[i]-Ha[i-m]*Base[m]==HashT)是否成立就可以了

  不过这道题的水分,就我写出来了,方法也是比较简单的,预处理出每个满足条件的区间,然后在dfs时存储选取的区间的末尾,再接下来选取,如果下一个区间的左端点大于dfs的末尾,就可以接上去,多一种方案。

30分水分代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define M 100086
#define P 1000000007
#define FOR(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
bool same[1050][1050];
struct node {
int L,R;
} block[M];
char A[M],B[M];
int n,m,t=0;
int top;
long long ans=0;
bool mark[M];
void dfs(int ed){
FOR(i,1,top)if(!mark[i]){
if(block[i].L>ed){
mark[i]=true;
ans++;
ans%=P;
dfs(block[i].R);
mark[i]=false;
}
}
}
void solve1() {
FOR(i,1,n)if(A[i]==B[1])FOR(j,1,i)FOR(k,i,n)same[j][k]=true;
top=0;
FOR(i,1,n)
FOR(j,i,n)if(same[i][j]){
top++;
block[top].L=i;
block[top].R=j;
}
dfs(0);
cout<<ans<<endl;
}
int main() {
scanf("%s %s",A+1,B+1);
n=strlen(A+1);
m=strlen(B+1);
solve1();
exit(0);
return 0;
}


AC代码:

#include<cstdio>
#include<cstring>
#define Add(A,B) A=(A+(B)%P)%P//非主流写法,请勿尝试!!!
#define M 100086
const int P=1e9+7,Hex=233;
char A[M],B[M];
int dp[M];
int sum[M],DP[M];
int n,m;
unsigned int Ha[M],Base[M],T;
int main() {
scanf("%s%s",A+1,B+1);
n=strlen(A+1);
m=strlen(B+1);
for(int i=1;i<=m;++i)T=T*Hex+B[i];
Base[0]=1;
for(int i=1;i<=m;++i)Base[i]=Base[i-1]*Hex;
for(int i=1;i<=n;++i)Ha[i]=Ha[i-1]*Hex+A[i];
for(int i=m;i<=n;++i){
if(Ha[i]-Ha[i-m]*Base[m]==T)Add(dp[i],sum[i-m]+i-m+1);
else dp[i]=dp[i-1];
Add(DP[i],DP[i-1]+dp[i]);
Add(sum[i],sum[i-1]+DP[i]);
}
printf("%d\n",DP
);
return 0;
}


第三题:Paths

  这道题有一个比较重要的性质:如果树上两条路径相交,那么必定有:LCA较深的路径的LCA会在LCA较浅的路径上,因此我们还可以得到一个性质:如果要选取两条路径,一定会选取LCA较深的路径,让改点对其他点的影响达到最小。(有点类似于区间调度问题)。

  因此我们可以按照路径LCA深度大小进行排序,如果已被标记过,就不取用,否则就把该区间的子树全都标记掉。若已被标记过就continue 掉。

  这道题的水分,二进制枚举也是比较好写的,由于代码长的让人恶心,就不介绍了。

  代码:

  

#include<cstdio>
#include<algorithm>
#include<vector>
#define M 100050
#define FOR(i,a,b) for(register int i=(a),i##_end_=(b);i<=i##_end_;++i)
#define DOR(i,a,b) for(register int i=(a),i##_end_=(b);i>=i##_end_;--i)
using namespace std;
int Fa[20][M],dep[M];
int n,m;
bool mark[M];
vector<int>edge[M];
void dfs(int x,int f) {
Fa[0][x]=f;
dep[x]=dep[f]+1;
FOR(i,0,edge[x].size()-1) {
int y=edge[x][i];
if(y==f)continue;
dfs(y,x);
}
}
void Init() {FOR(j,1,17)FOR(i,1,n)Fa[j][i]=Fa[j-1][Fa[j-1][i]];}
void Up(int &b,int step) {FOR(i,0,17)if(step&(1<<i))b=Fa[i][b];}
int LCA(int a,int b) {
if(dep[a]>dep[b])swap(a,b);
Up(b,dep[b]-dep[a]);
if(a==b)return a;
DOR(j,17,0)if(Fa[j][a]!=Fa[j][b])a=Fa[j][a],b=Fa[j][b];
return Fa[0][a];
}
struct node {
int fr,to,lca;
void Init() {lca=LCA(fr,to);}
bool operator <(const node &A)const {return dep[lca]>dep[A.lca];}
} A[M];
void Mark(int x,int f) {
mark[x]=true;
FOR(i,0,edge[x].size()-1){
int y=edge[x][i];
if(y==f)continue;
if(mark[y])continue;
Mark(y,x);
}
}
int main() {
scanf("%d%d",&n,&m);
FOR(i,1,n-1) {
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs(1,0);
Init();
FOR(i,1,m) {
scanf("%d%d",&A[i].fr,&A[i].to);
A[i].Init();
}
sort(A+1,A+m+1);
int ans=0;
FOR(i,1,m) {
if(mark[A[i].fr]||mark[A[i].to])continue;
Mark(A[i].lca,Fa[0][A[i].lca]);
++ans;
}
printf("%d\n",ans);
return 0;
}


总结:

  这次考得不是很好,但是,该水的分都拿来了,第一题虽说对拍半天没出结果,但是60分多少水过来了,都算是好事。不过第二题的递推没想到还是可惜了。接下来要看到这种题目要多往递推的方面想,不要整体考虑,而是要分成小问题来逐个分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: