NOIP2017模拟赛8
2017-06-20 14:59
176 查看
前言
为什么总结只有1和8,没有2、3、4、5、6、7呢?因为只有这两次是AK才有时间写啊!a路径
题目很长,简单一点来说就是一个二维平面坐标系中有N个整数点,主人公Bessie,每一步可以走上下左右四个点,要求你从(0,0)出发,走过所有给定的点,且结束点是任意一个给定的点,问是否有一个方案所有步数之和的奇偶性为P。分析
这题作为一道签到题,想一下就会发现这道题唯一的条件“步数之和”只与结束点有关,与其他都是没有关系的,所以对于每一个点,将它的坐标x和y加起来判断奇偶即可。程序
就不贴了吧。b冠军
有N个拳手参加比赛,分析
这题什么乱七八糟的方法(其实大部分都是在N!的方法上改进的)我都想过,最后发现除了状压加搜索优化以外,没有什么更好的方法。f[i][j][P]表示到了第i轮比赛,胜者是第j个拳手,P表示哪几位拳手参加了比赛。
搜索dfs(k,s,w,p)表示搜索到前k个拳手已经搜索完,一共有s个拳手被选中,其中胜利的是第w个拳手,这s个拳手是这p个。
其中第w个拳手一定要输给第j个拳手,且第j个拳手不能在这s个拳手中。
为了节省时间,一开始我还把有效的P找出来,但现在分析一想,并没有优化什么太多时间复杂度。
这题如果时间开1s确实很卡。。
#include <iostream> #include <algorithm> #include <stdio.h> #include <stdlib.h> #include <cmath> #include <cstring> #include <vector> #include <time.h> using namespace std; long long f[6][20][70000]; int N,w,P,n,m,sl[20],b[20][70000],a[20][20],tep[20]; char st[20]; void dfs(int k,int s,int p,int lw) { if ((s<<1)==(1<<N)) { if (lw==-1) return; f [w][P]+=f[N-1][lw][p]*f[N-1][w][P-p]*2; return ; } for (int i=k;i<=(1<<N);i++) { if (lw==-1&&a[w][tep[i]]) dfs(i+1,s+1,p|(1<<tep[i]),tep[i]); if (tep[i]!=w) dfs(i+1,s+1,p|(1<<tep[i]),lw); } } int main(){ freopen("b.in","r",stdin); freopen("b.out","w",stdout); scanf("%d",&n); for (int i=0;i<n;i++){ scanf("%s",&st); for (int j=0;j<n;j++) if (st[j]=='N') a[i][j]=0;else a[i][j]=1; } int l=1<<n; for (register int i=0;i<l;i++) { int i1=i,s=0; while (i1>0) {++s;i1-=i1&-i1;} if (s==2) s=1; else if (s==4) s=2; else if (s==8) s=3; else if (s==16) s=4; else s=0; if (s!=0){ sl[s]++;b[s][sl[s]]=i; } } for (int i=0;i<n;i++) for (int j=0;j<n;j++) if (a[i][j]) f[1][i][(1<<i)+(1<<j)]=2; if (n==2) m=1;if (n==4) m=2;if (n==8) m=3;if (n==16) m=4; for (int i=2;i<=m;i++) for (register int j=1;j<=sl[i];j++) { int cnt=0; for (int k=0;k<n;k++) if (b[i][j]&(1<<k)) tep[++cnt]=k; P=b[i][j];N=i; for (int k=1;k<=cnt;k++){ w=tep[k]; dfs(1,0,0,-1); } } for (int i=0;i<n;i++) printf("%lld\n",f[m][i][(1<<n)-1]); return 0; }
中间的
if (n==2) m=1;if (n==4) m=2;if (n==8) m=3;if (n==16) m=4;为什么用log(n)/log(2)算不出来,求大神解答。
小结一波
这道题的确卡了我非常多时间,首先一开始我认为状态转移需要很多时间,粗略一点就是216,就算是运用组合数减少转移状态的时间也不会好到哪里去。我没有算C,导致我以为C816很大,不比216少多少,算出来也证明只比216少6倍,但在这种卡常数的题中,少6倍就足够了。C指纹
给你n组四元组{a,b,c,d},其中如果每一个四元组A中的元素,有至少3个比另一个四元组B中的元素要小,则认为四元组B是“累赘”的。问有哪几组四元组是累赘的。分析
由于第二题花了比较多的时间,所以这一题我想得并不是很深入。首先n很大,就已经卡掉了很多譬如网络流之类的乱七八糟的算法。
对于本题而言,nn√和nlog2n的时间复杂度都是可以接受的,很容易想去数据结构和分块方面。其中分块我没想过,也不好想,所以我直接想数据结构方面了。(毕竟现在的oier数据结构都学得很好,当然蒟蒻我只会线段树)
一共有四维数据。首先肯定要先对第一维排序,这样子就可以减少一维数据的干扰。
然后问题来了,对于每一组四元组,你可以有两种方法进行处理,一种是判断这个四元组是不是“累赘”,另一种是用这个四元组来判断前面的四元组是不是“累赘”。由于第二种方法具有不确定性,相对来说第一种方法看上去简单很多。(对于暴力来说,两种方法并没有什么区别)
然后想如何不用排序的方式来判第二维的大小,然后就想到以数组下标为基础的桶排,然后用数据结构询问比第二维小的有没有就可以了。
然后考虑第三维,看着这个数据结构,我很惊奇地发现,这颗线段树里空空如也,我们就可以把第三维记进去。
既然这样,那也顺便把第四维也记进线段树里面。
总的来说,就是以第一维排序,那么先加入数据结构里面的a一定比当前a要小。然后在在线段树中1~b中找最小的c和d,最后判断即可。
这种方法还有一个缺陷,就是某一个四元组是“累赘”的,但a比较小,你没有把它视为“累赘”,所以这样做完一次以后,将c、d和a、b交换再做同样的操作即可。
程序
#include <iostream> #include <algorithm> #include <stdio.h> #include <stdlib.h> #include <cmath> #include <cstring> #include <vector> #include <time.h> using namespace std; struct node{ int a,b,c,d,e; }a[100010]; int T[2][400010],n; bool ans[100010]; bool cmp(node A,node B) { return A.a<B.a; } void update(int p,int ro,int L,int R,int x,int v) { if (L==R&&R==x) {T[p][ro]=v; return;} if (L>x||R<x) return; int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1; update(p,zuo,L,mid,x,v); update(p,you,mid+1,R,x,v); T[p][ro]=min(T[p][zuo],T[p][you]); } int query(int p,int ro,int L,int R,int le,int ri) { if (le<=L&&R<=ri) return T[p][ro]; if (R<le||L>ri) return n+1; int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1; int A=query(p,zuo,L,mid,le,ri), B=query(p,you,mid+1,R,le,ri); return min(A,B); } void work(){ sort(a+1,a+1+n,cmp); for (int i=0;i<=4*n;i++) T[0][i]=T[1][i]=n+1; update(0,1,1,n,a[1].b,a[1].c); update(1,1,1,n,a[1].b,a[1].d); for (int i=2;i<=n;i++) { int A=query(0,1,1,n,1,a[i].b), B=query(1,1,1,n,1,a[i].b); if (A<a[i].c||B<a[i].d) ans[a[i].e]=false; update(0,1,1,n,a[i].b,a[i].c); update(1,1,1,n,a[i].b,a[i].d); } } int main(){ freopen("c.in","r",stdin); freopen("c.out","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) ans[i]=true; for (int i=1;i<=n;i++) { scanf("%d%d%d%d",&a[i].a,&a[i].b,&a[i].c,&a[i].d); a[i].e=i; } work(); for (int i=1;i<=n;i++) { swap(a[i].a,a[i].c); swap(a[i].b,a[i].d); } work();int cnt=0; for (int i=1;i<=n;i++) if (!ans[i]) cnt++; printf("%d\n",cnt); for (int i=1;i<=n;i++) if (!ans[i]) printf("%d\n",i); return 0; }
小结
这题我联想起gdoi2016的某一天的第二题,做暴力并用抽屉原理来优化,但好像本题不是用这个方法。——-zero镇楼
相关文章推荐
- 2017.04.15【NOIP2017提高组】模拟赛B组题解
- NOIP2017提高组 模拟赛18(总结)
- NOIP2017模拟赛 约瑟夫游戏(数学乱搞)
- NOIP2017模拟赛 龙珠(dp+单调队列优化)
- {小结}2017.04.15【NOIP2017提高组】模拟赛B组
- 2017.08.18【NOIP2017提高组A组】模拟赛
- 清北学堂 NOIP2017模拟赛 越赛越心塞
- 2017.04.15【NOIP2017提高组】模拟赛B组总结
- NOIP2017提高组模拟赛 7(总结)
- 2017.08.18【NOIP2017提高组A组】模拟赛
- 【NOIP2017模拟赛】构造 A+B Problem(好题)
- NOIP2017提高组 模拟赛13(总结)
- 【NOIP2017模拟赛】思维+转化+图论 徒然Children(好题)
- 2017.04.15【NOIP2017提高组】模拟赛B组 总结
- NOIP2017提高组 模拟赛23(总结)
- NOIP2017提高组 模拟赛 26(总结)
- 2017.04.15【NOIP2017提高组】模拟赛B组
- NOIP2017提高组 模拟赛15(总结)
- 2017.04.15【NOIP2017提高组】模拟赛B组
- AtCoder AGC001F Wide Swap && NOIP2017模拟赛10.8T2