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分多少水过来了,都算是好事。不过第二题的递推没想到还是可惜了。接下来要看到这种题目要多往递推的方面想,不要整体考虑,而是要分成小问题来逐个分析。相关文章推荐
- 经验分享:迅雷离线下载无线端设计总结
- 2017-9-25离线赛总结
- Test20171009 考试总结 NOIP模拟赛
- 2017-10-9离线赛总结
- 离线清除LINUX系统密码总结
- 2017.10.19离线赛总结
- win8离线安装.net 3.5【总结-非原创】
- 2017.10.27离线赛总结
- 2017-10-29离线赛总结
- 离线缓存与客户端存储总结
- 2017-11-3离线赛总结
- 2017-11-5离线赛总结(NOIP七连测第三场)
- 2017-11-8离线赛总结
- 2017.10.29离线赛总结
- 2017-10-12离线赛总结
- 2017.10.11离线赛总结
- 2017-11-6离线赛总结
- 2017-10-16离线赛总结
- 2017-10-17离线赛总结
- 2017-10-6离线赛总结