码迷,mamicode.com
首页 > 其他好文 > 详细

2020.2.2 题解报告

时间:2020-02-03 22:04:14      阅读:86      评论:0      收藏:0      [点我收藏+]

标签:最小   一个   ant   next   报告   dig   ace   pac   check   


2020.2.2 题解报告

\[\text{R6真TM好玩, 考试的时候玩更好玩}\]
\[\text{By:Rainbow⑥ Block}\]

答题情况:

  • 总成绩 : 336 , 排名 : 3 / 10
  • T1 : 100 T2 : 36 T3 : 100

各题目分析:

  • 题目 1 :
    预估成绩 : 100 实际成绩 : 100 考试用时 : 2:45 ~ 3:21

    花费了45min的时间 考虑各题目 , T1先打了表,找到了优美的性质 ,
    \(n\sqrt{n}\)求欧拉函数之前写过,很快实现

  • 题目 2 :
    预估成绩 : 30 实际成绩 : 36 考试用时 : 4:26 ~ 5 : 20

    先打了30分的暴力, 之后考虑是否可以优化.
    找到了很多没用的性质, 最后没有优化成功.

  • 题目 3 :
    预估成绩 : 100 实际成绩 : 100 考试用时 : 3 : 25 ~ 4 : 20

    01 trie树贪心的模板, 之前写过, 很快实现.


题目解析:

T1 :

写了个爆搜打了个表
发现下列性质:

  1. 质数的答案都是 质数大小 - 1.

先盲猜一波欧拉函数,
虽然不会证, 从答案上看没什么问题.
n <= 1e15, \sqrt n 可过
sqrt n 求欧拉函数正好写过www.

若 1 把信写给 x,那么 2 必须把信写给 2x,
以此类推,y 必须把信写个 yx,所以我们要求 x,2x,3x,...,nx 恰好取遍 1...n.
这等价于 x 与 n 互质,所以我们只要求不超过 n 且与 n 互质
的自然数个数,这就是求欧拉函数.


T2 :

C(i) = 约数个数

手玩一下样例:
N = 5:
C(i) = 1, 2, 2, 3, 2
显然, 题目要求约数个数最大的数
由于 C(i) > max{ C(j) }, 1 <= j < i, 要严格大于
若存在两个约数个数最大的数, 选择较小的.

然后可以写出一个O(N sqrt(N))的暴力

若一个数 i 满足条件,我们观察它的唯一分解式 p1^a1 * p2^a2 * ...... * pk^ak,
则 p1,p2,...,pk 一定是前 k 个质数,并且 a1>=a2>=...>=ak,有了这些限制,我们就可以进行搜索,
求出小于 n 的因子数个数最多的数(因子数同样多则选择值最小的).


T3 :

01trie 贪心.
和做过的一道题很像.

  • 异或 , \(a[i] \leqslant 2^{30}\) :
    为什么数据范围要写成这个样子? 再考虑异或运算...
    想到进行二进制拆分

    \(a[i]\) 拆分后 , 贪心就可以方便地进行
    枚举两 \(01\) 串上 每一位上的数 , 尽量使 各位 相异 或后为 \(0\)
    即可选择出最合适的 \(b[i]\)

    • 用什么来维护 拆分后的二进制数 ?
      可以将二进制数 , 当成字符串来处理
      想到建立一棵字典树
      由于只有 \(0,1\) 两字符,所以是一棵二叉树
      根据 字典树 的性质 , 可以很方便地完成上述操作 .

算法实现:

先建立一棵基于各 \(b[i]\) 的二进制数 的字典树
并记录 各节点代表的 元素出现的个数 \(cnt\)

每次查询操作 , 将 \(a[i]\) 作参数进行传递
从字典树根部 , 开始向下, 查找每一层 (即每一二进制位) 的元素 ,
都尽量使 \(a[i],b[i]\) 此二进制位相异或为 \(1\)

若最优元素在此位置个数为 \(0\) ,选择另一元素
并进入下一层 , 继续进行查找 .

根据字典树的性质 ,
每次查询操作 必然可以找到 一个满足局部最优的 , 最适合 \(a[i]\)\(b[i]\)
查询 \(n\) 次后 , 即可找到最优的答案

注意的点:

为方便运算 ,
要保证 字典树 中 ,二进制数按照正向存储

也就是说,
如果使用位运算符进行二进制拆分, 要先拆出高位 , 再拆出低位
详见代码


代码实现:

T1 :

  • 考场代码 (正解):
//知识点:数论 
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <vector>
#define ll long long
const ll MARX = 1e15 + 10; 
//=================================================
ll N, ans;
std :: vector <ll> p, a;
//=================================================
inline ll read()
{
    ll w = 0, f = 1; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = - 1;
    for(;  isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
    return f * w;
}
//=================================================
int main()
{
    N = ans = read();
    for(ll i = 2, cnt = 0; i * i <= N; i ++, cnt = 0)
      if(N % i == 0)
      {
        ans /= i, ans *= (i - 1) ;
        while(N % i == 0) cnt ++, N /= i;
        p.push_back(i); a.push_back(cnt); 
      }
    if(N > 1) ans /= N, ans *= (N - 1), p.push_back(N), a.push_back(1);
    printf("%lld", ans);
    return 0;
}
/*
#include <cstdio>
#include <algorithm>
#include <ctype.h>
#define min std::min
#define max std::max
#define ll long long
const int MARX = 1010;
//=============================================================
int N, ans, tar[MARX];
bool use[MARX];
//=============================================================
inline int read()
{
    int f = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
    return f * w;
}
void check()
{
    for(int i = 1; i <= N; i ++)
      for(int j = i + 1; j <= N; j ++)
      {
        int now = (i + j) - ((i + j) > N) * N;
        int nowtar = tar[i] + tar[j] - ((tar[i] + tar[j]) > N) * N;
        if(tar[now] != nowtar) return ; 
      }
    ans ++;
}
void dfs(int now)
{
    if(now == N + 1) {check(); return ;}
    for(int i = 1; i <= N; i ++)
      if(! use[i])
      {
        tar[now] = i, use[i] = true;
        dfs(now + 1);
        use[i] = false;
      }
}
//=============================================================
int main()
{
    for(int i = 1; i <= 100; i ++)
    {
      N = i, ans = 0;
      dfs(1);
      printf("%d %d\n", i, ans); 
    }
}
*/ 

T2:

  • 考场代码:
//知识点:数论 
/*
By:Luckyblock
*/
#include <cstdio>
#include <algorithm>
#include <ctype.h>
#include <vector>
#define min std::min
#define max std::max
#define ll long long
//=============================================================
int N, Ans, Max;
//=============================================================
inline int read()
{
    int s = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') s = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 1) + (w << 3) + (ch ^ '0');
    return s * w;
}
int Get_yueshu(int now)
{
    int ret = 0;
    for(int i = 1; i * i <= now; i ++)
      if(now % i == 0) ret += 1 + (i != (now / i));
    return ret;
}
//=============================================================
int main()
{
    int N = read();
    for(int i = 1; i <= N; i ++)
    {
      int ret = Get_yueshu(i);
      if(Max < ret) Ans = i, Max = ret; 
    }
    printf("%d", Ans);
    return 0;
}

  • 正解 :
#include <cstdio>
#include <cstring>
#define E 510
using namespace std;

typedef long long LL;

LL n, ans;

int prime[E], b[E], cnt = 0;

int maxn;

void dfs(int v, int ended, int now, LL vel)
{
    if (now > maxn || now == maxn && vel < ans)
    {
        ans = vel; maxn = now;
    }
    for (int i = 1; i <= ended; i++)
    {
        LL z = prime[v];
        if (z*vel > n) break;
        vel *= z;
        dfs(v+1, i, now*(i+1), vel);
    }
}

int main()
{
    freopen("au0.in", "r", stdin);
    freopen("au0.out", "w", stdout);
    
    memset(b, 0, sizeof(b));
    for (int i = 2; i <= 500; i++)
    {
        if (!b[i]) 
        {
            prime[++cnt] = i;
            for (int j = i*2; j <= 500; j += i) b[j] = 1;
        }
    }
    scanf("%lld", &n);
    if (n <= 2) { printf("%lld\n", n); return 0; }
    
    maxn = 0;
    for (int i = 1; i <= 30; i++) dfs(1, i, 1, 1);
    printf("%lld\n", ans);
    
    
    return 0;
}

T3:

  • 考场代码(正解):
//知识点:01trie, 贪心 
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define ll long long
const int mod = 16777216;
const int MARX = 2e6 + 10;
//==================================================
struct node
{
    bool cnt;
    int son[2]; //存每个节点代表元素出现次数 , 两儿子 
} trie[20 * MARX];
ll N, K, B, P, num = 1, D[MARX];
ll Mul, Ans;
//==================================================
inline int read()
{
    int w = 0, f = 1; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = - 1;
    for(;  isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
    return f * w;
}
inline void Insert(int key) //建树 
{
    int now = 1; //从根节点开始,向下查找 
    trie[1].cnt = 1;
    for(int i = 30; i >= 0; i --) //将二进制数正向加入字典树 
    {
      int next = (key >> i) & 1ll; //当前位数 
      if(! trie[now].son[next]) trie[now].son[next] = ++ num; //添加新节点 
      now = trie[now].son[next];
      trie[now].cnt = 1; //增加出现次数 
    }
}
inline int Query(int key) //回答询问 
{
    int now = 1, ans = 0; //从根部查找 
    for(int i = 30; i >= 0; i --) 
    {
      int nowbit = (key >> i) & 1; //当前位 
      if(trie[now].son[nowbit ^ 1]) ans += (1ll << i),now = trie[now].son[nowbit ^ 1];
      else now = trie[now].son[nowbit];
    }
    return ans;
}
//==================================================
int main()
{
    N = read(), K = read(), B = read(), P = read();
    for(int i = 1; i <= N; i ++) 
    {
      if(i == 1) D[i] = P;
      else D[i] = (K * D[i - 1] % mod + B % mod) % mod;
      Insert(D[i]);
    }
    for(int i = 1; i <= N; i ++)
    {
      if(i == 1) Mul = 1;
      else Mul = Mul * 3 % mod;
      Ans = (Ans + Mul * Query(D[i]) % mod) % mod;
    }
    printf("%lld", (Ans + mod) % mod);
    return 0;
}

2020.2.2 题解报告

标签:最小   一个   ant   next   报告   dig   ace   pac   check   

原文地址:https://www.cnblogs.com/luckyblock/p/12257477.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!