NOIP模拟题 2016.10.18 [二分答案] [从上到下的树形DP] [链表翻转]
2016-10-19 10:58
381 查看
T1:
题意:给定x正半轴和y正半轴上的n个点,依次按顺序连接,保证线段不相交。m次询问,询问每个点与原点的连线与这些线段有多少个交点。
二分答案。。
每次分到一个线段check一下点和线段的位置关系即可。
T2:
题意:有根树根结点为1,有些节点有若干种票(v,k,w),表示在v节点可以经过道路条数为k,票价为w的一种票。任意时刻最多一张票,但是可以随时撕掉手上的票。多次询问从节点u到根节点的最小费用。
80%做法:
记忆化搜索,dp[u][k]表示当前在u节点,手上还有k个道路的票,要到根节点需要花费的最小票价。
那么从上向下更新即可,用map保存状态。
100%做法:
dp[u]表示在u节点买票,最终到达根节点的最小票价。
设在u节点当前某种票为Ku,Wu
那么dp[u] = min{ dp[v] + Wu } , 其中v是u的祖先节点,并且depth[u] - depth[v] <= Ku
倍增或树链剖分或LCT维护u节点到祖先节点的dp值得最小值即可。
T3:
题意:实现一个文本编辑器,有两个光标,支持光标左移右移,在光标前面插入一个字符,在光标后边删除一个字符,反转左右光标之间的文本,输出当前文本串。(可以操作输出’T’,不能操作输出’F’)
见到反转似乎是Splay的拿手好戏,然而带上光标就不知所措了。。。
100%做法:
用链表维护,如果用类似于Splay打标记的方法还是不能过完。
考虑不打标记。。。
如果翻转区间,就把区间首尾的两个节点更新,其他的不变,相当于延迟标记。
如果发现 后继的后继的前驱 不是 后继,那么就把后继的后继的两个指针交换,前驱同理。
由于反转之后除了首尾两个节点的其他节点都是未处理的,那么当发现冲突时,就把冲突下传,类似于电信号的传递。。。。。。
题意:给定x正半轴和y正半轴上的n个点,依次按顺序连接,保证线段不相交。m次询问,询问每个点与原点的连线与这些线段有多少个交点。
二分答案。。
每次分到一个线段check一下点和线段的位置关系即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { if(ch=='-') flag = -1; ch = getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch-'0'; ch = getchar(); } x *= flag; } const double eps = 1e-8; const LL INF = (1ll<<60); inline int dcmp(double x) { if(fabs(x) < eps) return 0; return x>0.0?1:-1; } const int maxn = 100005; struct Line { LL x,y; }line[maxn]; int n,q; LL xx,yy; inline bool check(int k) // point below line { double Y = -(double)line[k].y/line[k].x*xx + (double)line[k].y; return dcmp(Y - (double)yy) == 1; } inline int work() { int l=1 , r=n+1; while(l<r) // the minimum line above P(x0,y0) { int m = (l+r)>>1; if(check(m)) r = m; else l = m + 1; } return l-1; } LL X[maxn],Y[maxn]; int main() { freopen("geometry.in","r",stdin); freopen("geometry.out","w",stdout); read(n); for(int i=1;i<=n;i++) read(X[i]); for(int i=1;i<=n;i++) read(Y[i]); sort(X+1,X+n+1); sort(Y+1,Y+n+1); for(int i=1;i<=n;i++) line[i]=(Line){X[i],Y[i]}; line[n+1] = (Line) {INF,INF}; read(q); while(q--) { read(xx); read(yy); int ans = work(); printf("%d",ans); if(q) putchar('\n'); } return 0; }
T2:
题意:有根树根结点为1,有些节点有若干种票(v,k,w),表示在v节点可以经过道路条数为k,票价为w的一种票。任意时刻最多一张票,但是可以随时撕掉手上的票。多次询问从节点u到根节点的最小费用。
80%做法:
记忆化搜索,dp[u][k]表示当前在u节点,手上还有k个道路的票,要到根节点需要花费的最小票价。
那么从上向下更新即可,用map保存状态。
100%做法:
dp[u]表示在u节点买票,最终到达根节点的最小票价。
设在u节点当前某种票为Ku,Wu
那么dp[u] = min{ dp[v] + Wu } , 其中v是u的祖先节点,并且depth[u] - depth[v] <= Ku
倍增或树链剖分或LCT维护u节点到祖先节点的dp值得最小值即可。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { if(ch=='-') flag = -1; ch = getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch-'0'; ch = getchar(); } x *= flag; } const LL INF = (1ll<<61); const int maxn = 100005; struct Edge { int to,next; }edge[maxn]; int head[maxn]; int maxedge; inline void addedge(int u,int v) { edge[++maxedge] = (Edge) { v,head[u] }; head[u] = maxedge; } struct Tick { int next; int k,w; }tick[maxn]; int tick_head[maxn]; int maxtick; inline void addtick(int v,int k,int w) { tick[++maxtick] = (Tick) { tick_head[v],k,w }; tick_head[v] = maxtick; } const int maxd = 20; const int D = 18; int fa[maxn][maxd]; LL dp[maxn][maxd]; LL ans[maxn]; int depth[maxn]; void dfs(int u,int father,int deep) { depth[u] = deep; if(~father) { fa[u][0] = father; dp[u][0] = ans[father]; } for(int k=1;k<=D;k++) { fa[u][k] = fa[fa[u][k-1]][k-1]; dp[u][k] = min(dp[u][k-1],dp[fa[u][k-1]][k-1]); } ans[u] = ~father? INF : 0; for(int i=tick_head[u];~i;i=tick[i].next) { Tick now = tick[i]; LL tmp = INF; int cur = u; for(int k=D;k>=0;k--) if((1<<k)<=now.k && depth[fa[cur][k]]>=depth[1]) smin(tmp,dp[cur][k]) , cur=fa[cur][k] , now.k-=(1<<k); smin(ans[u],tmp+now.w); } for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; dfs(v,u,deep+1); } } int n,m; inline void init() { memset(head,-1,sizeof(head)); maxedge=-1; memset(tick_head,-1,sizeof(tick_head)); maxtick=-1; read(n),read(m); for(int i=1;i<n;i++) { int x,y; read(x),read(y); addedge(y,x); } for(int i=1;i<=m;i++) { int v,k,w; read(v),read(k),read(w); addtick(v,k,w); } dfs(1,-1,1); } void work() { int q; read(q); while(q--) { int tmp; read(tmp); printf("%d",ans[tmp]); if(q) putchar('\n'); } } int main() { freopen("party.in","r",stdin); freopen("party.out","w",stdout); init(); work(); return 0; }
T3:
题意:实现一个文本编辑器,有两个光标,支持光标左移右移,在光标前面插入一个字符,在光标后边删除一个字符,反转左右光标之间的文本,输出当前文本串。(可以操作输出’T’,不能操作输出’F’)
见到反转似乎是Splay的拿手好戏,然而带上光标就不知所措了。。。
100%做法:
用链表维护,如果用类似于Splay打标记的方法还是不能过完。
考虑不打标记。。。
如果翻转区间,就把区间首尾的两个节点更新,其他的不变,相当于延迟标记。
如果发现 后继的后继的前驱 不是 后继,那么就把后继的后继的两个指针交换,前驱同理。
由于反转之后除了首尾两个节点的其他节点都是未处理的,那么当发现冲突时,就把冲突下传,类似于电信号的传递。。。。。。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<climits> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) template <class T> inline void read(T &x) { x = 0; int flag = 1; char ch = getchar(); while(ch<'0' || ch>'9') { if(ch=='-') flag = -1; ch = getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch-'0'; ch = getchar(); } x *= flag; } const int INF=0x3f3f3f3f; const int maxn = 10000005; struct Node { int pre,nxt; char val; inline void clear() { pre=nxt=0; val='\0'; } }node[maxn]; #define pre(x) node[x].pre #define nxt(x) node[x].nxt #define val(x) node[x].val int maxnode; int _begin,_end; int pos[2],cnt[2]; // two pointers queue <int> que; inline int require() { if(!que.empty()) { int tmp = que.front(); que.pop(); return tmp; } return ++maxnode; } inline void recycle(int x) { que.push(x); node[x].clear(); } char s[maxn]; inline void init() { scanf("%s",s); int lens = strlen(s); _begin=1,_end=2; maxnode=2; for(int i=0;i<lens;i++) { val(++maxnode) = s[i]; pre(maxnode) = i==0? _begin : maxnode-1; nxt(maxnode) = i==lens-1? _end : maxnode+1; } pre(_begin) = -1; nxt(_begin) = 2 + 1; pre(_end) = 2 + lens+1; nxt(_end) = -1; pos[0] = _begin; pos[1] = maxnode; cnt[0] = 1; cnt[1] = lens + 1; } void _move_left(int op) { if(pos[op] == _begin) { putchar('F'); return; } int u = pos[op] , v = pre(u); if(nxt(v)^u) swap(pre(v),nxt(v)); // reverse flag pushing down pos[op] = v , cnt[op]--; putchar('T'); } void _move_right(int op) { if(nxt(pos[op]) == _end) { putchar('F'); return; } int u = nxt(pos[op]) , v = nxt(u); if(pre(v)^u) swap(pre(v),nxt(v)); pos[op] = u , cnt[op]++; putchar('T'); } void _insert(int op,char c) { int u = pos[op] , v = nxt(u); int now = require(); 1369d val(now) = c; pre(now) = u; nxt(now) = v; nxt(u) = now; pre(v) = now; if(cnt[op^1]>=cnt[op]) cnt[op^1]++; pos[op] = now; cnt[op]++; if(pos[op^1] == u) pos[op^1] = now; // two pointers at the same position putchar('T'); } void _delete(int op) { if(nxt(pos[op]) == _end) { putchar('F'); return; } int u = pos[op] , v = nxt(u) , w = nxt(v); if(pre(w)^v) swap(pre(w),nxt(w)); recycle(v); nxt(u) = w; pre(w) = u; if(cnt[op^1]>cnt[op]) cnt[op^1]--; // only if op^1 is upper than op if(pos[op^1] == v) pos[op^1] = u; // case of the same position putchar('T'); } void _reverse() { if(cnt[1]<=cnt[0]) { putchar('F'); return; } if(cnt[1] == cnt[0]+1) // no effects to reverse { putchar('T'); return; } int p1 = pos[0] , p2 = nxt(p1) , p3 = pos[1] , p4 = nxt(p3); // increasing permutation swap(pre(p2),nxt(p2)); swap(pre(p3),nxt(p3)); nxt(p1) = p3; pre(p3) = p1; nxt(p2) = p4; pre(p4) = p2; pos[1] = p2; // R cursor moving putchar('T'); } void _show() { int root = _begin; do { if(pre(nxt(root))^root) swap(pre(nxt(root)),nxt(nxt(root))); root = nxt(root); putchar(val(root)); }while(nxt(root)^_end); } inline char Read() { char ch = getchar(); while(ch==' ' || ch=='\n') ch = getchar(); return ch; } int main() { freopen("editor.in","r",stdin); freopen("editor.out","w",stdout); init(); int q; read(q); while(q--) { char op = Read(); char tmp; switch(op) { case '<': _move_left(Read()=='R'); break; case '>': _move_right(Read()=='R'); break; case 'I': tmp=Read(); _insert(tmp=='R',Read()); break; case 'D': _delete(Read()=='R'); break; case 'R': _reverse(); break; case 'S': _show(); break; } if(q) putchar('\n'); } return 0; }
相关文章推荐
- NOIP模拟题 2016.10.5 [Trie] [数学] [二分答案] [杂题] [复杂状态DP]
- 【NOIP 模拟题】[T2] 王者荣耀(二分答案+dp)
- 【noip模拟题】[dp][二分][树链剖分][hdu5029][线段树]
- BZOJ_2097_[Usaco2010 Dec]Exercise 奶牛健美操_二分答案+树形DP
- NOIP模拟题 [DP][二分][树剖][树上差分]
- bzoj 4753: [Jsoi2016]最佳团体 二分答案+树形dp
- 【NOIP2017普及组T4】跳房子-二分答案+DP单调队列优化
- 【NOIP 模拟题】[T2]宝藏(树形dp)
- NOIP模拟赛 军训(二分答案+单调队列优化DP)
- BZOJ2282 SDOI2011消防/NOIP2007树网的核(二分答案+树形dp)
- 【bzoj2097】[Usaco2010 Dec]Exercise 奶牛健美操 二分答案+树形dp+贪心
- NOIP模拟题 2016.9.3 [数论] [逆序对] [树状数组] [树形dp]
- 湖南NOIP集训模拟题DAY1 BY ExfJOE [贪心][DP][二分]
- BZOJ 2067 POI 2004 SZN 树形DP 贪心 二分答案
- BZOJ 2097 USACO 2010 Dec Gold Exercise 奶牛健美操 二分答案 树形DP 贪心
- 【NOIP 模拟题】刺杀大使(二分答案+并查集)
- hdu3586(树形DP+二分答案)
- 【HDU3586】Information Disturbing-二分答案+树形DP
- 【NOIP 模拟题】[T2]拯救紫萱学姐(kmp+树形dp)
- [BZOJ 2500]幸福的道路 树形dp+单调队列+二分答案