【NOIP模拟题】【二分】【倍增】【链表】【树规】

3 计算几何
3.1 题意描述
花花对计算几何有着浓厚的兴趣。他经常对着平面直角坐标系发呆,思考一些有趣的问
题。今天,他想到了一个十分有意思的题目:
首先,花花会在x 轴正半轴和y 轴正半轴分别挑选n 个点。随后,他将x 轴的点与y 轴
的点一一连接,形成n 条线段,并保证任意两条线段不相交。花花确定这种连接方式有且仅
有一种。最后,花花会给出m 个询问。对于每个询问,将会给定一个点P(xp; yp),问线段
OP(O 为坐标原点)与n 条线段会产生多少个交点?
3.2 输入格式
第1 行包含一个正整数n,表示线段的数量;
第2 行包含n 个正整数,表示花花在x 轴选取的点的横坐标;
第3 行包含n 个正整数,表示花花在y 轴选取的点的纵坐标;
第4 行包含一个正整数m,表示询问数量;
随后m 行,每行包含两个正整数xp 和yp,表示询问中给定的点的横、纵坐标。
3.3 输出格式
共m 行,每行包含一个非负整数,表示你对这条询问给出的答案。
3.4 样例输入
3
4 5 3
3 5 4
2
1 1
3 3
3.5 样例输出
0
3
3.6 样例解释
3 条线段分别为:(3; 0) ? (0; 3)、(4; 0) ? (0; 4)、(5; 0) ? (0; 5)
(0; 0) ? (1; 1) 不与他们有交点,答案为0。

(0; 0) ? (3; 3) 与三条线段均有交点,答案为3。
3.7 数据规模与约定
• 对于40% 的数据:n;m <=10;
• 另有20% 的数据:n;m <=100;
• 另有20% 的数据:n;m <=1000;
• 对于100% 的数据:n;m <=10^5; 1 <= x; y <= 2^31。


4 花花的聚会
4.1 题意描述
花花住在H 国。H 国有n 个城市,其中1 号城市为其首都。城市间有n ? 1 条单向道
路。从任意一个城市出发,都可以沿着这些单向道路一路走到首都。事实上,从任何一个城市
走到首都的路径是唯一的。
过路并不是免费的。想要通过某一条道路,你必须使用一次过路券。H 国一共有m 种过
路券,每张过路券以三个整数表示:v k w:你可以在城市v 以价格w 买到一张过路券。这
张券可以使用k 次。这意味着,拿着这张券通过了k 条道路之后,这张券就不能再使用了。
请注意你同一时间最多只能拥有最多一张过路券。但你可以随时撕掉手中已有的过路券,
并且在所在的城市再买一张。
花花家在首都。他有q 位朋友,他希望把这些朋友们都邀请到他家做客。所以他想要知道
每位朋友要花多少路费。他的朋友们都很聪明,永远都会选择一条花费最少的方式到达首都。
花花需要准备晚餐去了,所以他没有时间亲自计算出朋友们将要花费的路费。你可以帮帮
他么?
4.2 输入格式
输入的第一行包含两个空格隔开的整数n 和m,表示H 国的城市数量和过路券的种数。
之后的n ? 1 行各自包含两个数ai 和bi,代表城市ai 到城市bi 间有一条单向道路。
之后的m 行每行包括三个整数vi; ki 和wi,表示一种过路券。
下一行包含一个整数q,表示花花朋友的数量。
之后的q 行各自包含一个整数,表示花花朋友的所在城市。
4.3 输出格式
输出共q 行,每一行代表一位朋友的路费。
4.4 样例输入
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
4.5 样例输出
10
22
5
4.6 样例解释
对于第一位朋友,他在5 号城市只能购买一种过路券,花费10 元并且可以使用3 次。这
足够他走到首都,因此总花费是10 元。
对于第二位朋友,他在6 号城市只能购买20 元的过路券,并且只能使用一次。之后,他
可以在3 号城市购买2 元,可以使用3 次的过路券走到首都。总花费是22 元。
对于第三位朋友,他在7 号城市可以购买两种过路券。他可以花3 元买一张可以使用2
次的券,然后在3 号城市再买一张2 元,可以使用3 次的券,走到首都。总花费是5 元,而且
其他的购买方式不会比这种更省钱。
4.7 数据规模与约定
• 对于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 <=105;wi <= 10000; 1 <= vi; ki <= n。


 

5 文本编辑器
5.1 题意描述
九发明了一个完美的文本编辑器。这个编辑器拥有两个光标(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”。
开始时文本编辑器中有一定内容,左光标在第一个字符左,右光标在最后一个字符右。
注意:在插入和删除操作中,没有被操作的光标与文本的相对左右位置保持不变。特别
地,若两个光标重叠,操作后也仍然重叠。
5.2 输入格式
第一行是初始时文本编辑器内容。

第二行是一个正整数N,N 表示操作次数。
接下来有N 行,每行有一个命令,命令格式如上方表格。
5.3 输出格式
对于每个命令,按上方表格要求执行并输出。
5.4 样例输入
goodykc
11
I R u
I R l
> L
> L
> L
> L
R
D R
< R
D R
S
5.5 样例输出
T
T
T
T
T
T
T
F
T
T
goodluck

5.6 样例解释
[goodykc]
[goodykcu]
[goodykcul]
g[oodykcul]
go[odykcul]
goo[dykcul]
good[ykcul]
good[lucky]
good[lucky](光标右边没有字符,失败删除)
good[luck]y
good[luck]
goodluck
5.7 数据规模与约定
• 对于40% 的数据:1 <= N , 初始文本长度 100,数据不包含翻转(Reverse)操作;
• 另有30% 的数据:1 <=N , 初始文本长度 10^5,数据不包含翻转(Reverse)操作;
• 另有20% 的数据:1 <=N , 初始文本长度 10^5,数据包含翻转(Reverse)操作;
• 对于100% 的数据:1 <=N , 初始文本长度 4 * 10^6,输出文件大小 20MB;


 

解题报告:

  第一题:要点:线性规划(数学判断);二分查找。连边的时候直接排序就可以了,然后再二分。把二分模板化。。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define maxn 100005
 6 using namespace std;
 7 int n,m,x[maxn],y[maxn],ans;
 8 struct pp{
 9     int xi,yi;
10 };
11 pp linee[maxn];
12 bool pd(int a,int b,int x)
13 {
14     double v=0,w=0;
15     v=(double)linee[x].yi/linee[x].xi*a;
16     w=(double)linee[x].yi-b;
17     if (v<w) return true;
18     else return false;
19 }
20 void doit(int a,int b)
21 {
22     int l=1,r=n;
23     while (l<r)
24     {
25         int mid=(l+r)>>1;
26         if (pd(a,b,mid)) r=mid;
27         else l=mid+1;
28     }
29     if (pd(a,b,l)) ans=l-1;
30     else ans=l;
31     return ;
32 }
33 int main()
34 {
35     freopen("geometry.in","r",stdin);
36     freopen("geometry.out","w",stdout);
37     cin>>n;
38     for (int i=1;i<=n;i++)
39       scanf("%d",&x[i]);
40     for (int i=1;i<=n;i++)
41       scanf("%d",&y[i]);
42     sort(x+1,x+1+n);
43     sort(y+1,y+1+n);
44     for (int i=1;i<=n;i++)
45     {
46         linee[i].xi=x[i];
47         linee[i].yi=y[i];
48     }
49     cin>>m;
50     for (int i=1;i<=m;i++)
51     {
52         int a,b;ans=0;
53         scanf("%d%d",&a,&b);
54         doit(a,b);
55         printf("%d\n",ans);
56     }
57     return 0;
58 }
View Code

  第二题:知道是树规,但是,不会。好吧,本来也想建边来做,结果放弃了,后来听说建边可以过80。。我的暴力只过了40,还是该写建边的。

       80%:把车票理解为从 v 城市可以到它的上面 k 个祖先城市,权值都是 w ,然后找最短路就可以了。

     100%:用dfs+倍增+dp。以前的倍增都是单独一个函数做的,现在和dfs一起,一边dp一边倍增。dp:这里的树规是从上往下dp的,和以往的不一样,意为从 u 节点到 1 节点的最小花费。然后倍增往上跳。把每个城市的票数作为循环,使用次数作为第二层循环,取其中最小值的最小值+当前车票的花费wi 。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<algorithm>
 6 #define maxn 100005
 7 #define inf 12345678
 8 using namespace std;
 9 int n,m,q;
10 int tot,he[maxn],to[maxn],ne[maxn],phe[maxn],toto;
11 int ans[maxn],depth[maxn],f[15][maxn*4],dp[15][maxn*4];
12 struct pp{
13     int next;
14     int ki,wi;
15 };
16 pp city[maxn];
17 void add(int a,int b)
18 {
19     tot++;to[tot]=b;ne[tot]=he[a];he[a]=tot;
20 }
21 void add2(int v,int k,int w)
22 {
23     city[++toto].next=phe[v];
24     city[toto].ki=k;
25     city[toto].wi=w;
26     phe[v]=toto;
27 }
28 void dfs(int x,int fa,int deep)//dfs嵌套倍增 
29 {
30     depth[x]=deep;
31     if (fa){
32         f[0][x]=fa;
33         dp[0][x]=ans[fa];
34     }
35     for (int i=1;i<=14;i++)
36     {
37         f[i][x]=f[i-1][f[i-1][x]];
38         dp[i][x]=min(dp[i-1][x],dp[i-1][f[i-1][x]]);
39     }
40     if (fa) ans[x]=inf;
41     else ans[x]=0;
42     for (int i=phe[x];i;i=city[i].next)
43     {
44         pp now=city[i];
45         int tmp=inf,cur=x;
46         for (int j=14;j>=0;j--)
47         if ((1<<j)<=now.ki&&depth[f[j][cur]]>=1){
48             tmp=min(tmp,dp[j][cur]);
49             cur=f[j][cur];
50             now.ki-=(1<<j);
51         }
52         ans[x]=min(ans[x],tmp+now.wi);
53     }
54     for (int i=he[x];i;i=ne[i])
55         dfs(to[i],x,deep+1);
56 }
57 int main()
58 {
59     freopen("party.in","r",stdin);
60     freopen("party.out","w",stdout);
61     cin>>n>>m;
62     for (int i=1;i<n;i++)
63     {
64         int a,b;
65         scanf("%d%d",&a,&b);
66         add(b,a);
67     }
68     for (int i=1;i<=m;i++)
69     {
70         int v,k,w;
71         scanf("%d%d%d",&v,&k,&w);
72         add2(v,k,w);
73     }
74     dfs(1,0,1);// now ,father ,depth
75     cin>>q;
76     for (int i=1;i<=q;i++)
77     {
78         int x;
79         scanf("%d",&x);
80         printf("%d\n",ans[x]);
81     }
82     return 0;
83 }
View Code

  第三题:链表。考试的时候写的暴力,结果没有过。链表没有学过,只好先从头开始。双向链表:对于每一个节点记一个前驱,一个后继。对于翻转,做延迟更新的处理,即只修改区间前后的四个节点的前驱后继,更新时只需判断此节点的前驱的后继是不是此节点,如果不是,就交换它的前驱后继,这个由画图可以得到。

   1、左移:用后继的后继的前驱不等于后继判断更新,因为光标所指的节点是光标的左边节点,所以如果移动左光标,意味着此光标所指的节点的后继的后继需要判断更新;同理,对于右移而言,它所指的节点也是它左边的节点,所以它只需要判断更新此节点的前驱。

   2、删除:需要判断当前节点。

   3、注意各个操作的光标修改。更多的注释写在代码里了。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<string>
  5 #define maxn 8000005  //not 4000005 因为还要insert 
  6 using namespace std;
  7 int n,m,len,idx;//idx:记录字符个数 
  8 int sur[maxn],pre[maxn],pos[2],cnt[2];
  9 //cnt:判断左右节点的相对位置,pos:左右节点的位置,pre:前驱,sur:后继 
 10 char ch[maxn],in[maxn/2],c,b,add;//ch:修改后的字符,in:原字符串 
 11 void init()
 12 {
 13     scanf("%s",in);len=strlen(in);
 14     pos[0]=pos[1]=cnt[0]=cnt[1]=1;
 15     pre[1]=-1;sur[1]=2;//将1、2位置空出来,1节点作为字符的开头,2节点作为字符的末尾 
 16     pre[2]=1;sur[2]=-1;//可以理解为一个环 但开头节点无前驱,尾节点无后继 
 17     idx=2;
 18     for (int i=0;i<len;i++)
 19     {
 20         ch[++idx]=in[i];
 21         pre[idx]= i==0 ? 1:idx-1;
 22         sur[idx]= i==len-1 ? 2:idx+1;
 23     }
 24     if (len>0){
 25         pos[1]=idx;//更新右节点位置 
 26         cnt[1]=idx+1;//更新右节点相对位置 
 27         sur[1]=3;
 28         pre[2]=idx;//更新尾节点的前驱 
 29     }
 30     cin>>n;
 31 }
 32 int pd()
 33 {
 34     getchar();b=getchar();
 35     return b=='L'? 0:1;
 36 }
 37 void move_left(int op)
 38 {
 39     if (pos[op]==1) putchar('F');//putchar 更快 注意是 ‘ ’ 
 40     else {
 41         int u=pos[op],v=pre[u];
 42         if (u!=sur[v]) swap(sur[v],pre[v]);
 43         pos[op]=v;
 44         cnt[op]--;
 45         putchar('T');
 46     }
 47     putchar('\n');
 48 }
 49 void move_right(int op)
 50 {
 51     if(sur[pos[op]]==2) putchar('F');
 52     else {
 53         int u=sur[pos[op]],v=sur[u];
 54         if(pre[v]!=u) swap(pre[v],sur[v]);
 55         pos[op]=u;//not v
 56         cnt[op]++;
 57         putchar('T');
 58     }
 59     putchar('\n');
 60 }
 61 void in_sert(int op)
 62 {
 63     getchar();add=getchar();
 64     int u=pos[op],v=sur[u];
 65     ch[++idx]=add;
 66     sur[idx]=v;
 67     pre[idx]=u;
 68     sur[u]=idx;
 69     pre[v]=idx;
 70     pos[op]=idx;
 71     if (pos[op^1]==u) pos[op^1]=idx;//重合移动 (op^1 即相反节点1->0,0->1) 
 72     if (cnt[op^1]>=cnt[op]) cnt[op^1]++;//如果另一个光标在op后面,那它的cnt也要改 
 73     putchar('T');
 74     putchar('\n');
 75 }
 76 void de_lete(int op)
 77 {
 78     if (sur[pos[op]]==2) putchar('F');
 79     else {
 80         int u=sur[pos[op]],v=sur[u];
 81         if (pre[v]!=u) swap(pre[v],sur[v]);
 82         sur[pos[op]]=v;
 83         pre[v]=pos[op];
 84         if (pos[op^1]==u) pos[op^1]=v;//***
 85         if (cnt[op^1]>cnt[op]) cnt[op^1]--;//***
 86         putchar('T');
 87     }
 88     putchar('\n');
 89 }
 90 void re_verase()
 91 {
 92     if (cnt[1]-cnt[0]<=0) putchar('F');//L 在 R  右边或重合
 93     else {
 94         if (cnt[1]-cnt[0]>1){
 95             int a=pos[0],b=sur[a],c=pos[1],d=sur[c];
 96             swap(pre[b],sur[b]);swap(pre[c],sur[c]);
 97             sur[a]=c;sur[b]=d;
 98             pre[c]=a;pre[d]=b;
 99             pos[1]=b;
100         }
101         putchar('T');
102     }
103     putchar('\n');
104 }
105 void shown()
106 {
107     int u=1;
108     while (true){
109         if (pre[sur[u]]!=u) swap(pre[sur[u]],sur[sur[u]]);
110         u=sur[u];
111         if (u==2) break;
112         putchar(ch[u]);
113     }
114     putchar('\n');
115 }
116 void work()
117 {
118     for (int i=1;i<=n;i++)
119     {
120         getchar();
121         c=getchar();
122         switch(c)
123         {
124             case'<':move_left(pd());break;
125             case'>':move_right(pd());break;
126             case'I':in_sert(pd());break;
127             case'D':de_lete(pd());break;
128             case'R':re_verase();break;
129             case'S':shown();break;
130         }
131     }
132 }
133 int main()
134 {
135     freopen("editor.in","r",stdin);
136     freopen("editor.out","w",stdout);
137     init();
138     work();
139     return 0;
140 }

 

转载于:https://www.cnblogs.com/lx0319/p/5978845.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值