您的位置:首页 > 理论基础 > 数据结构算法

NOIP模拟题 2016.10.18 [二分答案] [从上到下的树形DP] [链表翻转]

2016-10-19 10:58 381 查看
T1:

题意:给定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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息