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

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();
    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;
}
原创文章 152 获赞 15 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ourfutr2330/article/details/52857519
今日推荐