2019牛客暑期多校训练营(第十场)

传送门

 

 

B.Coffee Chicken(递归)

•题意

  定义 S1 = "COFFEE" , S2 = "CHICKEN";

  Si = Si-2 + Si-1

  求在 Sn 中,以第 k 个字符开始的连续 10 个字符;

  k ≤ min( |Sn| , 1012);

•Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 
 5 int n;
 6 ll k;
 7 ll f[80];
 8 string s[3];
 9 
10 string F(ll l,ll r,int pos)
11 {
12 //    printf("pos=%d,(%lld,%lld)\n",pos,l,r);
13     if(pos <= 2)
14         return s[pos].substr(l,r-l+1);
15 
16     ll mid=f[pos-2];
17 
18     string ans;
19     if(r <= mid)
20         return F(l,r,pos-2);
21     else if(l > mid)
22         return F(l-mid,r-mid,pos-1);
23     else
24     {
25 //        ans=F(l,mid,pos-2)+F(1,r-mid,pos-1);
26         ans=F(l,mid,pos-2);
27         ans += F(1,r-mid,pos-1);
28     }
29     return ans;
30 }
31 string Solve()
32 {
33     if(n > 60)
34         n=(n&1 ? 59:60);
35 
36     cout<<F(k,min(k+9,f[n]),n)<<endl;
37 }
38 void Init()
39 {
40     s[1]="#COFFEE";
41     s[2]="#CHICKEN";
42     f[1]=6;
43     f[2]=7;
44     for(int i=3;i <= 60;++i)
45         f[i]=f[i-1]+f[i-2];
46 }
47 int main()
48 {
49     Init();
50 
51     int T;
52     cin>>T;
53     while(T--)
54     {
55         cin>>n>>k;
56         Solve();
57     }
58 }
View Code

疑惑:

  将第 26,27 行代码合并成第 25 行代码;

  在递归时,按理说应该是先递归第一个 F(l,mid,pos-2) 然后在递归 F(1,r-mid,pos-1);

  但实际输出中间结果的是否,发现,先递归了 F(1,r-mid,pos-1) 在递归了 F(l,mid,pos-2);

  虽然最终结果依旧是先递归 F(l,mid,pos-2) 的结果,但是,为什么中间过程不对呢?

    

    (图一)     (图二)

  图一是未合并 26,27 时的中间过程,pos = 3 后,先调用 pos = 1 再调用 pos = 2;

  图二是合并后的中间过程,pos = 3 后,先调用了 pos = 2;

  但是两者输出的最终结果却相同,提交后也能 AC,这是为啥????

 


 

E.Hilbert Sort(思维 and 坐标变换)

•题意

  

         $k = 1$            $k = 2$           $k = 3$

  可以将边长为 2k 的正方形平分成四部分:①左上角②左下角③右下角④右上角;

  k 阶希尔伯特曲线可由 k-1 阶希尔伯特曲线推出;

  (1)k-1 阶希尔伯特曲线按照主对角线反转得到①部分;

  (2)k-1 阶希尔伯特曲线拷贝到②③部分;

  (3)k-1 阶希尔伯特曲线按照副对角线反转得到④部分;

  如上图所示,分别表示 1阶,2阶,3阶 希尔伯特曲线;

  现给出你 n 个坐标和 k,让你根据 k阶 希尔伯特曲线的走向排列给出的 n 个坐标;

•题解

  根据递推条件易得,将任意 k 阶希尔伯特曲线划分成四部分①②③④(意义如上所述);

  其曲线走向都和 1阶 希尔伯特曲线的走向相同 ①->②->③->④;

  对于求解 k 阶希尔伯特曲线的 n 个点的走向可以转化为求解 ①②③④ 对应的 k-1 阶的点的走向;

  具体步骤如下;

  步骤一:

    将当前的点划分成四部分;

  步骤二:

    对于每一部分的点,根据上述(1)(2)(3)的逆变化将其变化成 k-1 阶对应的坐标;

    对于 ①:因为从 k-1阶 到 ① 做的变化为(1),那么,从 ① 到 k-1阶 需要做的变化为将 (x,y) 变为 (y,x);

    对于 ②:由(2)可得,只需将 (x,y) 变为 (x-2k-1,y) 即可;

    对于 ③:由(2)可得,只需将 (x,y) 变为 (x-2k-1,y-2k-1) 即可;

    对于 ④:由(3)可得,只需将 (x,y) 变为 (2k-y+1,2k-1-x) 即可;

  步骤三:

    根据走向的优先级,优先调用 ① 的 k-1阶,跳转到步骤一,知道 k-1 = 0 为止;

    其次调用 ② , ③ , ④ 的 k-1 阶;

•Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e6+50;
 4 
 5 int n,k;
 6 struct Data
 7 {
 8     int id;
 9     int x,y;
10 }a[maxn];
11 vector<Data >p;
12 
13 void Go(int k,vector<Data >p)
14 {
15     if(p.empty())
16         return ;
17     if(k == 0)
18     {
19         assert(p.size() == 1);
20         printf("%d %d\n",a[p[0].id].x,a[p[0].id].y);
21         p.clear();
22         return ;
23     }
24     int siz=1<<(k-1);
25     vector<Data >q[5];
26     for(int i=0;i < p.size();++i)
27     {
28         int id,x,y;
29         id=p[i].id;
30         x=p[i].x;
31         y=p[i].y;
32 
33         if(x <= siz && y <= siz)
34             q[1].push_back(Data{id,y,x});
35         else if(x > siz && y <= siz)
36             q[2].push_back(Data{id,x-siz,y});
37         else if(x > siz && y > siz)
38             q[3].push_back(Data{id,x-siz,y-siz});
39         else
40             q[4].push_back(Data{id,2*siz-y+1,siz-x+1});
41     }
42     for(int i=1;i <= 4;++i)
43         Go(k-1,q[i]);
44 }
45 int main()
46 {
47     scanf("%d%d",&n,&k);
48     for(int i=1;i <= n;++i)
49     {
50         int x,y;
51         scanf("%d%d",&x,&y);
52         a[i]={i,x,y};
53         p.push_back(a[i]);
54     }
55     Go(k,p);
56 
57     return 0;
58 }
坐标变换

疑惑:

  加上第 19 行的 assert(p.size() == 1 ) 就能 AC,不加会返回 MLE;

  Why ????

•Code2

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define ll long long
 4 const int maxn=1e6+50;
 5 
 6 int n,k;
 7 struct Data
 8 {
 9     string id;
10     int x,y;
11     bool operator < (const Data &obj)const
12     {
13         return id < obj.id;
14     }
15 }a[maxn];
16 
17 string Go(int x,int y,int k)
18 {
19     if(k == 0)
20         return "0";
21 
22     int siz=1<<(k-1);
23     ll hs=1ll<<(2*k);
24     if(x <= siz && y <= siz)
25         return "1"+Go(y,x,k-1);
26     if(x > siz && y <= siz)
27         return "2"+Go(x-siz,y,k-1);
28     if(x > siz && y > siz)
29         return "3"+Go(x-siz,y-siz,k-1);
30 
31     return "4"+Go(2*siz-y+1,siz-x+1,k-1);
32 }
33 int main()
34 {
35 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
36     scanf("%d%d",&n,&k);
37     for(int i=1;i <= n;++i)
38     {
39         int x,y;
40         scanf("%d%d",&x,&y);
41         string id=Go(x,y,k);
42         a[i]={id,x,y};
43     }
44     sort(a+1,a+n+1);
45     for(int i=1;i <= n;++i)
46         printf("%d %d\n",a[i].x,a[i].y);
47 }
坐标变换+hash

  参考自rank1代码:? TQL;

  我是用字符串 hash 的,相比巨巨代码,慢了好多好多:

    

  而巨巨代码的运行时间:

    

  差的也太多了吧,tql,然鹅,hash 这块我并没怎么学过,是时候学学 hash 了;

 

转载于:https://www.cnblogs.com/violet-acmer/p/11387077.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值