noip模拟题10.20

啊又有很久没有写博客了,好吧事实上昨天才写了这篇,结果被csdn吃了一大半,只有今天重写。这次的题很有价值,所以抽时间把博客发了。

t1 计算几何

题意描述
花花对计算几何有着浓厚的兴趣。他经常对着平面直角坐标系发呆,思考一些有趣的问题。今天,他想到了一个十分有意思的题目:首先,花花会在x 轴正半轴和y 轴正半轴分别挑选n 个点。随后,他将x 轴的点与y 轴的点一一连接,形成n 条线段,并保证任意两条线段不相交。花花确定这种连接方式有且仅有一种。最后,花花会给出m 个询问。对于每个询问,将会给定一个点P(xp; yp),问线段OP(O 为坐标原点)与n 条线段会产生多少个交点?

输入格式
第1 行包含一个正整数n,表示线段的数量;
第2 行包含n 个正整数,表示花花在x 轴选取的点的横坐标;
第3 行包含n 个正整数,表示花花在y 轴选取的点的纵坐标;
第4 行包含一个正整数m,表示询问数量;
随后m 行,每行包含两个正整数xp 和yp,表示询问中给定的点的横、纵坐标。

输出格式
共m 行,每行包含一个非负整数,表示你对这条询问给出的答案。

样例输入
3
4 5 3
3 5 4
2
1 1
3 3

样例输出
0
3

样例解释

3 条线段分别为:(3; 0) - (0; 3)、(4; 0) - (0; 4)、(5; 0) - (0; 5)
(0; 0) - (1; 1) 不与他们有交点,答案为0。
(0; 0) - (3; 3) 与三条线段均有交点,答案为3。

数据规模与约定
• 对于40% 的数据:n;m 10;
• 另有20% 的数据:n;m 100;
• 另有20% 的数据:n;m 1000;
• 对于100% 的数据:n;m 1e5; 1 x; y < 231。

简单二分,思路应该很好想到。不过要注意二分的边界问题。我常用的模板是:

while(l<r)
{
    …………
}
printf("%d",r);

不知为何反正这样不会错。
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
ll a[100001],b[100001];
int main()
{
    freopen("geometry.in","r",stdin);
    freopen("geometry.out","w",stdout);
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf(AUTO,&a[i]);
    for(int i=1;i<=m;i++)
        scanf(AUTO,&b[i]);
    sort(a+1,1+m+a);
    sort(b+1,1+m+b);
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int s=1,t=m;
        ll x,y;
        scanf(AUTO,&x);
        scanf(AUTO,&y);
        while(s<=t)
        {
            int mid=(s+t)>>1;
            if(x*b[mid]+y*a[mid]-a[mid]*b[mid]>0)
                s=mid+1;
            else if(x*b[mid]+y*a[mid]-a[mid]*b[mid]<0)
                t=mid-1;
            else
            {
                t=mid;
                break;
            }
        }
        printf("%d\n",t);
    }
    return 0;
}

花花的聚会

题意描述
花花住在H 国。H 国有n 个城市,其中1 号城市为其首都。城市间有n �� 1 条单向道路。从任意一个城市出发,都可以沿着这些单向道路一路走到首都。事实上,从任何一个城市走到首都的路径是唯一的。过路并不是免费的。想要通过某一条道路,你必须使用一次过路券。H 国一共有m 种过路券,每张过路券以三个整数表示:v k w:你可以在城市v 以价格w 买到一张过路券。这张券可以使用k 次。这意味着,拿着这张券通过了k 条道路之后,这张券就不能再使用了。请注意你同一时间最多只能拥有最多一张过路券。但你可以随时撕掉手中已有的过路券,并且在所在的城市再买一张。花花家在首都。他有q 位朋友,他希望把这些朋友们都邀请到他家做客。所以他想要知道每位朋友要花多少路费。他的朋友们都很聪明,永远都会选择一条花费最少的方式到达首都。花花需要准备晚餐去了,所以他没有时间亲自计算出朋友们将要花费的路费。你可以帮帮他么?

输入格式
输入的第一行包含两个空格隔开的整数n 和m,表示H 国的城市数量和过路券的种数。之后的n-1行各自包含两个数ai 和bi,代表城市ai 到城市bi 间有一条单向道路。

之后的m 行每行包括三个整数vi; ki 和wi,表示一种过路券。
下一行包含一个整数q,表示花花朋友的数量。

之后的q 行各自包含一个整数,表示花花朋友的所在城市。

输出格式
输出共q 行,每一行代表一位朋友的路费。

样例输入
7 7
3 1
2 1
7 6
6 3
5 3
4 3
7 2 3
7 1 1
2 3 5
3 6 2
4 2 4
5 3 10
6 1 20
3
5
6
7

样例输出
10
22
5

样例解释
对于第一位朋友,他在5 号城市只能购买一种过路券,花费10 元并且可以使用3 次。这足够他走到首都,因此总花费是10 元。对于第二位朋友,他在6 号城市只能购买20 元的过路券,并且只能使用一次。之后,他可以在3 号城市购买2 元,可以使用3 次的过路券走到首都。总花费是22 元。对于第三位朋友,他在7 号城市可以购买两种过路券。他可以花3 元买一张可以使用2次的券,然后在3 号城市再买一张2 元,可以使用3 次的券,走到首都。总花费是5 元,而且
其他的购买方式不会比这种更省钱。

数据规模与约定
• 对于40% 的数据:n; m; q 10;wi 10;
• 另有20% 的数据:n; m; q 500;wi 100;
• 另有20% 的数据:n; m; q 5000;wi 1000;
• 对于100% 的数据:n; m; q 1e5;wi 10000; 1 vi; ki n。

考试时直接码暴力,过了6个点。想过是动归,dp[i]表示 i 城市到达根节点的最小代价,枚举 i 城市的过路劵,在 i -该过路劵所能到达的城市中,dp[i]=dp[u]+w,w为该过路劵的价格。

方程应该很好理解,那么我们看数据:n、m、q极值到了十的五次方,直接枚举肯定是过不了的,这时就要考虑倍增。

从根节点向下dp,记一个数组dis[i][j]表示 i 节点向上跳2^j步中dp的最小值,如下图:

for(int i=1;i<=16;i++)
    {
        fa[k][i]=fa[fa[k][i-1]][i-1];
        dis[k][i]=min(dis[k][i-1],dis[fa[k][i-1]][i-1]);
    }

然后就可以实现dp的方程(用邻接链表储存每个城市的过路劵)

while(Tov[t])
    {
        int step=Tov[t];
        int w=cost[t];
        int minx=0x7f7f7f7f;
        int pos=k;
        for(int i=0;i<=16;i++)
        {
            if(step&(1<<i))//保证跳到step步 
            {
                minx=min(minx,dis[pos][i]);
                pos=fa[pos][i];
            }
        }
        dp[k]=min(dp[k],minx+w);
        t=Nex[t];
    }

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int tot,cnt,head[100001],Head[100001],Tov[100001],tov[200001];
int dp[100001],nex[200001],Nex[100001];
int fa[100001][20],dis[100001][20],cost[100001];
void add(int a,int b)
{
    tov[++tot]=b;
    nex[tot]=head[a];
    head[a]=tot;
}
void ticketadd(int a,int b,int c)
{
    Tov[++cnt]=b;
    cost[cnt]=c;
    Nex[cnt]=Head[a];
    Head[a]=cnt;
}
void dfs(int k,int FA)
{
    dis[k][0]=dp[FA];
    fa[k][0]=FA;
    for(int i=1;i<=16;i++)
    {
        fa[k][i]=fa[fa[k][i-1]][i-1];
        dis[k][i]=min(dis[k][i-1],dis[fa[k][i-1]][i-1]);
    }
    int t=Head[k];
    while(Tov[t])
    {
        int step=Tov[t];
        int w=cost[t];
        int minx=0x7f7f7f7f;
        int pos=k;
        for(int i=0;i<=16;i++)
        {
            if(step&(1<<i))//保证跳到step步 
            {
                minx=min(minx,dis[pos][i]);
                pos=fa[pos][i];
            }
        }
        dp[k]=min(dp[k],minx+w);
        t=Nex[t];
    }
    t=head[k];
    while(tov[t])
    {
        if(tov[t]!=FA)
            dfs(tov[t],k);
        t=nex[t];
    }
}
int main()
{
    freopen("party.in","r",stdin);
    freopen("party.out","w",stdout);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        ticketadd(a,b,c);
    }
    memset(dp,0x7f7f7f7f,sizeof dp);
    memset(dis,0x7f7f7f7f,sizeof dis);
    dp[1]=0;
    dfs(1,0);
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int a;
        scanf("%d",&a);
        printf("%d\n",dp[a]);
    }
}

t3 文本编辑器

题意描述
九发明了一个完美的文本编辑器。这个编辑器拥有两个光标(cursor),所以九能够同时在两处地方插入和删除文本。这个编辑器除了正常的编辑功能以外,还有一些只有九才知道用处的功能,例如翻转两个光标之间的文本。某一天,九把自己的完美文本编辑器给弄丢了,但是她还有好多好多文本需要处理。于是她想请聪明又智慧的你帮她实现完美文本编辑器的一些功能。
功能列表如下:
功能名称命令格式说明
‘<’ (move left) ‘< w’
w 为一个字符,“L”或“R”,表示左光标还是右光标(下同)。
该命令将选定光标向左移动,如果已经是最左端则不移动。
命令执行成功时输出“T”,若光标已经在最左端,则输出“F”。

‘>’(move right) ‘> w’
w 同上。
与< 命令不同的是,该命令将光标向右移动。
命令执行成功时输出“T”,若光标已经在最右端,则输出“F”。

‘I’ (insert) ‘I w c’
w 同上。
c 是一个可见字符(33 ascii 码 126),代表在该光标左侧插入该字符。
该命令始终输出“T”。

‘D’ (delete) ‘D w’
w 同上。
代表删除该光标右侧的一个字符。
命令执行成功时输出“T”,若光标右侧没有字符输出“F”。

‘R’ (reverse) ‘R’
代表翻转左光标和右光标之间的字符。
该命令只有左光标在右光标左侧时才能执行。
(两光标重合时也不能执行)
命令执行成功时输出“T”,否则输“F”。

‘S’ (show) ‘S’ 代表显示当前处理的文本。
该命令只输出文本,不输出“T”和“F”。
开始时文本编辑器中有一定内容,左光标在第一个字符左,右光标在最后一个字符右。
注意:在插入和删除操作中,没有被操作的光标与文本的相对左右位置保持不变。特别地,若两个光标重叠,操作后也仍然重叠。

输入格式
第一行是初始时文本编辑器内容。
6
NOIP 模拟Day1 5. 文本编辑器
第二行是一个正整数N,N 表示操作次数。
接下来有N 行,每行有一个命令,命令格式如上方表格。

输出格式
对于每个命令,按上方表格要求执行并输出。

样例输入
goodykc
11
I R u
I R l
L
L
L
L
R
D R
< R
D R
S

样例输出
T
T
T
T
T
T
T
F
T
T
goodluck

样例解释
[goodykc]
[goodykcu]
[goodykcul]
g[oodykcul]
go[odykcul]
goo[dykcul]
good[ykcul]
good[lucky]
good[lucky](光标右边没有字符,失败删除)
good[luck]y
good[luck]
goodluck

数据规模与约定
• 对于40% 的数据:1 N , 初始文本长度 100,数据不包含翻转(Reverse)操作;
• 另有30% 的数据:1 N , 初始文本长度 1e5,数据不包含翻转(Reverse)操作;
• 另有20% 的数据:1 N , 初始文本长度 1e5,数据包含翻转(Reverse)操作;
• 对于100% 的数据:1 N , 初始文本长度 4×1e6,输出文件大小 20MB;

这道题的话…考试我还是建议码暴力,因为想不出正解或者想出了正解但代码难度较大,有限时间内无法保证正确率。(我当然码的暴力…)
这道题用链表做,就是那啥pre啊next啊什么的,这算是我第一次接触这类题目。链表的思路很好理解,但难以理解的是reserve操作。每次reserve操作,我们只把光标之间最近的两个交换
a|dafdss|f 只交换a、d、s、f的前驱后继,中间的在进行其他操作的时候交换前驱后继。
代码:

#include<queue>
#include<cstdio>
#include<iostream>
#include<cstring>
#define val(x) gg[x].val
#define pre(x) gg[x].pre
#define nex(x) gg[x].nex
using namespace std;
queue<int>q;
const int maxn=1e7+5;
char T[maxn];
struct node{
    int pre,nex;
    char val;
    inline void clear() {pre=nex=0; val='\0';}
}gg[maxn];
int con[2],tmp[2],dex,begin,end;
inline void recycle(int x)
{
    q.push(x);
    gg[x].clear();
}
inline int require()
{
    if(!q.empty())
    {
        int t=q.front();
        q.pop();
        return t;
    }
    return ++dex;
}
inline char read()
{
    int cc=getchar();
    while(cc==' '||cc=='\n')cc=getchar();
    return cc;
}
inline void lmove(int p)
{
    if(tmp[p]==begin)
    {
        putchar('F');
        return ;
    }
    int u=tmp[p],v=pre(u);
    if(nex(v)^u)swap(pre(v),nex(v));
    tmp[p]=v;con[p]--;
    putchar('T');
}
inline void rmove(int p)
{
    if(nex(tmp[p])==end)
    {
        putchar('F');
        return ;
    }
    int u=nex(tmp[p]),v=nex(u);
    if(pre(v)^u)swap(pre(v),nex(v));
    tmp[p]=u;con[p]++;
    putchar('T');
}
inline void insert(int p,char cc)
{
    int u=tmp[p],v=nex(u);
    int t=require();
    pre(t)=u;nex(t)=v;
    nex(u)=t;pre(v)=t;
    val(t)=cc;
    if(con[p^1]>=con[p])con[p^1]++;
    tmp[p]=t;con[p]++;
    if(tmp[p^1]==u)tmp[p^1]=t;
    putchar('T');
}
inline void Delete(int p)
{
    if(nex(tmp[p])==end)
    {
        putchar('F');
        return ;
    }
    int u=tmp[p],v=nex(u),w=nex(v);
    if(pre(w)^v)swap(nex(w),pre(w));
    recycle(v);
    nex(u)=w;pre(w)=u;
    if(con[p^1]>con[p])con[p^1]--;
    if(tmp[p^1]==v)tmp[p^1]=u;
    putchar('T');
}
inline void reserve()
{
    if(con[0]>=con[1])
    {
        putchar('F');
        return ;
    }
    if(con[0]+1==con[1])
    {
        putchar('T');
        return ;
    }
    int p1=tmp[0],p2=nex(p1),p3=tmp[1],p4=nex(p3);
    swap(pre(p2),nex(p2));swap(pre(p3),nex(p3));
    nex(p1)=p3;pre(p4)=p2;
    nex(p2)=p4;pre(p3)=p1;
    tmp[1]=p2;
    putchar('T');
}
inline void show()
{
    int root=begin;
    while(nex(root)!=end)
    {
        if(pre(nex(root))^root)swap(pre(nex(root)),nex(nex(root)));
        root=nex(root);
        putchar(val(root));
    }
}
int main()
{
    freopen("editor.in","r",stdin);
    freopen("editor.out","w",stdout);
    scanf("%s",T);
    int len=strlen(T);
    begin=1;end=2;dex=2;
    for(int i=0;i<len;i++)
    {
        val(++dex)=T[i];
        pre(dex)=dex-1;
        nex(dex)=dex+1;
    }
    pre(begin)=-1;nex(begin)=3;
    pre(end)=dex;nex(end)=-1;
    pre(3)=begin;nex(dex)=end;
    tmp[0]=1;tmp[1]=dex;
    con[0]=0;con[1]=len;
    int q;
    scanf("%d",&q);
    while(q--)
    {
        char op=read();
        char tt;
        switch(op)
        {
            case '<':lmove(read()=='R');break;
            case '>':rmove(read()=='R');break;
            case 'I':tt=read();insert(tt=='R',read());break;
            case 'D':Delete(read()=='R');break;
            case 'R':reserve();break;
            case 'S':show();break;
        }
        putchar('\n');
    }
    return 0;
}

第三题价值较大,注意复习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值