[ACdream原创群赛の数树]解题报告

比赛地址

ACdream原创群赛の数树


递推数

题目叙述

求递推数列的a[a[a[a[n]]]]

解法

方法是暴力。。。

一开始用一个暴力的代码(复杂度 O(n))跑 1min 左右跑出来 a[n] 对于 1e9+7 的循环节 222222224LL , 然后继续跑出来 222222224LL 的循环节 183120LL 然后跑出来 183120LL 的循环节 240LL。于是整个问题就简单了, 矩阵快速幂算出第n位mod 240 , 然后再改mod 为 183120LL ,然后再改成 222222224LL ,然后再改成 1e9 + 7..


Code

暴力代码:

(偷懒暴力代码也用矩阵写的。。。)

/*
 * =====================================================================================
 *
 *         Author:  KissBuaa.DS(AC)
 *        Company:  BUAA-ACMICPC-Group
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <stdbool.h>
#include <math.h>
#define LL long long
#define CLR(x) memset(x,0,sizeof(x))
#define typec LL
#define sqr(x) ((x)*(x))
#define abs(x) ((x)<0?(-(x)):(x))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define PI acos(-1.0)
#define lowbit(x) ((x)&(-(x)))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define inf 100000000
//For C++
#include <cctype>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <bitset>
#include <list>
#include <iostream>
using namespace std;
const double eps=1e-10;
int dblcmp(typec d) {
    if (fabs(d)<eps)
       return 0;
    return (d>0)?1:-1;
}
LL modo;
const int maxn=3;
class Mat
{
public:
       LL Matn,Matm;
       typec a[maxn][maxn];
       Mat()
       {
            Matn=0;
            Matm=0;
            memset(a,0LL,sizeof(a));
       }
       void output();
       void init();
       void initI();
       Mat mul(const Mat &a);
       Mat power(const Mat&a,LL k);
};
void Mat::output()
{
     for (int i=0;i<Matn;++i)
     {
         for (int j=0;j<Matm;++j)
         {
             if (j!=0) printf(" ");
             printf("%I64d",a[i][j]);
         }
         printf("\n");
     }
}
void Mat::init()
{
    Matn=0;
    Matm=0;
    memset(a,0LL,sizeof(a));
}
Mat Mat::mul(const Mat &A)
{
    Mat c;
    c.init();
    c.Matn=Matn;
    c.Matm=A.Matm;
    for (int i=0;i<Matn;++i)
        for (int j=0;j<A.Matm;++j)
        {
            for (int k=0;k<Matm;++k)
            {
                c.a[i][j]=(c.a[i][j]+(a[i][k]*A.a[k][j])%modo)%modo;
            }
        }
    return c;
}
void Mat::initI()
{
     memset(a,0LL,sizeof(a));
     for (int i=0;i<Matn;++i) a[i][i]=1LL;
}
Mat Mat::power(const Mat& a,LL  k)
{
    Mat c=a,b;
    b.init();
    b.Matn=a.Matn;b.Matm=a.Matm;
    b.initI();
    while (k)
    {
         if (k & (1LL))
            b=b.mul(c);
         c=c.mul(c);
         k>>=1LL;
    }
    return b;
}
Mat  A ,B;
LL g(LL x){
    //Mat A;
    if (x == 0LL) return 0LL;
    if (x == 1LL) return 1LL;
    //A.init();
    //A.Matn = 1LL , A.Matm = 2LL;
    //A.a[0][0]=1LL,A.a[0][1]=0LL;
    //Mat B;
    B.init();
    B.Matn = 2LL , B.Matm = 2LL;
    B.a[0][0]= 3LL, B.a[1][0]=1LL;
    B.a[0][1] = 1LL; B.a[1][1]=0LL;
    B = B.power(B , x - 1LL);
    //A = A.mul(B);
    return B.a[0][0];
}
LL n;
set< pair<LL , LL> > has;
#define MP make_pair
void solve(){
    has.clear();
    LL i;
    LL pre = g(1);
    for (i = 2 ; ; ++i){
        LL res = g(i);
        if(has.find( MP(pre , res) ) != has.end()){
            cout<<i - 2<<endl;
            return;
        }
        has.insert( MP(pre , res) );
        pre = res;
    }
}

int main(){
    modo = 1e9 + 7;
    solve();
//    freopen("0.in" , "r" , stdin);
//    freopen("0.out" , "w" , stdout);
//    int T;
//    cin >> T;
//    while (T--) solve();
//    while(~scanf("%I64d" , &n)) solve();
}

于是得到了这个之后就能用

快速幂代码:

/*
 * =====================================================================================
 *
 *         Author:  KissBuaa.DS(AC)
 *        Company:  BUAA-ACMICPC-Group
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <stdbool.h>
#include <math.h>
#define LL long long
#define CLR(x) memset(x,0,sizeof(x))
#define typec LL
#define sqr(x) ((x)*(x))
#define abs(x) ((x)<0?(-(x)):(x))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define PI acos(-1.0)
#define lowbit(x) ((x)&(-(x)))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define inf 100000000
//For C++
#include <cctype>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <bitset>
#include <list>
#include <iostream>
using namespace std;
const double eps=1e-10;
int dblcmp(typec d) {
    if (fabs(d)<eps)
       return 0;
    return (d>0)?1:-1;
}
LL modo;
const int maxn=3;
class Mat
{
public:
       LL Matn,Matm;
       typec a[maxn][maxn];
       Mat()
       {
            Matn=0;
            Matm=0;
            memset(a,0LL,sizeof(a));
       }
       void output();
       void init();
       void initI();
       Mat mul(const Mat &a);
       Mat power(const Mat&a,LL k);
};
void Mat::output()
{
     for (int i=0;i<Matn;++i)
     {
         for (int j=0;j<Matm;++j)
         {
             if (j!=0) printf(" ");
             printf("%I64d",a[i][j]);
         }
         printf("\n");
     }
}
void Mat::init()
{
    Matn=0;
    Matm=0;
    memset(a,0LL,sizeof(a));
}
Mat Mat::mul(const Mat &A)
{
    Mat c;
    c.init();
    c.Matn=Matn;
    c.Matm=A.Matm;
    for (int i=0;i<Matn;++i)
        for (int j=0;j<A.Matm;++j)
        {
            for (int k=0;k<Matm;++k)
            {
                c.a[i][j]=(c.a[i][j]+(a[i][k]*A.a[k][j])%modo)%modo;
            }
        }
    return c;
}
void Mat::initI()
{
     memset(a,0LL,sizeof(a));
     for (int i=0;i<Matn;++i) a[i][i]=1LL;
}
Mat Mat::power(const Mat& a,LL  k)
{
    Mat c=a,b;
    b.init();
    b.Matn=a.Matn;b.Matm=a.Matm;
    b.initI();
    while (k)
    {
         if (k & (1LL))
            b=b.mul(c);
         c=c.mul(c);
         k>>=1LL;
    }
    return b;
}
Mat  A ,B;
LL g(LL x){
    //Mat A;
    if (x == 0LL) return 0LL;
    if (x == 1LL) return 1LL;
    //A.init();
    //A.Matn = 1LL , A.Matm = 2LL;
    //A.a[0][0]=1LL,A.a[0][1]=0LL;
    //Mat B;
    B.init();
    B.Matn = 2LL , B.Matm = 2LL;
    B.a[0][0]= 3LL, B.a[1][0]=1LL;
    B.a[0][1] = 1LL; B.a[1][1]=0LL;
    B = B.power(B , x - 1LL);
    //A = A.mul(B);
    return B.a[0][0];
}
LL n;
void solve(){
    scanf("%lld",  &n);
    LL res;
    //printf("%I64d\n",g(42837));
    modo = 240LL;
    res = g(n);

    modo = 183120LL;
    res = g(res);
    //res = (res * inv(res , modo))% modo;
    //cout<<res<<endl;
    modo = 222222224LL;
    res = g(res);
    //cout<<res<<endl;
    //res = (res * inv(res , modo))% modo;
    modo =(1000000000LL + 7LL);
    res = g(res);
    //res = (res * inv(res-1LL , modo))% modo;
    //printf("%I64d\n",g(g(g(n))));
    printf("%lld\n",res);
}/*
set< pair<LL , LL> > has;
#define MP make_pair
void solve(){
    has.clear();
    LL i;
    LL pre = g(1);
    for (i = 2 ; ; ++i){
        LL res = g(i);
        if(has.find( MP(pre , res) ) != has.end()){
            cout<<i - 2<<endl;
            return;
        }
        has.insert( MP(pre , res) );
        pre = res;
    }
}
*/
int main(){
//    modo = 12LL;
    solve();
//    freopen("0.in" , "r" , stdin);
//    freopen("0.out" , "w" , stdout);
    int T;
    cin >> T;
    while (T--) solve();
//    while(~scanf("%I64d" , &n)) solve();
}

这题比较贱的是我故意选择了答案是 0 和 1 的答案,发现好多啊。。。

这是个原题,我觉得太好玩儿了就拿过来了。。。


郭式树

题目描述:

求 abs(x - y)

解法:

longlong 的 x - y 会溢出,所以只要特判加起来是 2 ^ 63 的两种情况即可。。。。

Code:

void solve(){
    LL a , b;
    RD(a , b);
    if (a == 4611686018427387904ll && b == -4611686018427387904ll)
        printf("9223372036854775808\n");
    else if (b == 4611686018427387904ll && a == -4611686018427387904ll)
        printf("9223372036854775808\n");
    else OT(abs(a - b));
}
int main(){
//#ifdef LOCAL
//    freopen("0.in" , "r" , stdin);
//    freopen("0.out" ,"w" , stdout);
//#endif
    Rush solve();
}

这题的高精度方法是被我故意卡掉的。。。


面面数

题目

求原问题答案 >= n 的最小的数

解法:

推出公式 + 二分,公式就是 n ^ 2 - n + 2, 然后特殊判断一下 n = 1 输出 0 的情况即可

Code:

LL n;
void solve(){
    RD(n);
    if (n == 1){
        puts("0");
        return;
    }
    LL low = 1 , high = 1e5 , mid , ret = high;
    do{
        mid = low + high >> 1;
        if (mid * mid - mid + 2 >= n){
            checkMin(ret , mid);
            high = mid - 1ll;
        }
        else low = mid + 1ll;
    }while(low <= high);
    OT(ret);
}
int main(){
//    freopen("0.in" , "r" , stdin);
//    freopen("0.out" , "w" , stdout);
    Rush solve();
}

平衡树

这题准备和 CF #173 E 一起讲。

题目:

求一个数在一堆数选一个数,最大的异或值

CF : 求数列不重叠前缀 & 后缀 最大异或值

解法:

用Trie 或者手写二叉树,来记录每个数字的每一个二进制位。

试想,对于一个数字x,在ai里面找最大的异或值,其实就是一个贪心的过程:

1、把x每一位取反(0->1 , 1 -> 0)

2、从最高位开始找,如果最高位和x取反的最高位相等,那么异或值的最高位就是 1 , 如果没有的话只能是 0 了。

3、不停滴做 2,于是就是在二叉树 / Trie 上面尽可能找和 1 之后的x尽量“匹配”的数字即可。

复杂度理论是O(n)

Code

struct node{
    int son[2] , value;
    void reset(){
        son[0] = -1 , son[1] = -1 , value = -1;
    }
}tree[32 * 10000];
int id;
int mm[32];
int dig[32];
char op[10];
void getdig(int x , int rev){
    for (int i = 0 ; i < 32 ; ++i)
        dig[i] = ((x & mm[31 - i]) != 0) ^ rev;
}
void insert(int x){
    getdig(x , 0);
    int root = 0;
    for (int i = 0 ; i < 32 ; ++i){
        if (tree[root].son[dig[i]] == -1){
            tree[root].son[dig[i]] = ++id;
            tree[id].reset();
        }
        root = tree[root].son[dig[i]];
    }
    tree[root].value = x;
}
int query(int x , int rev){
    getdig(x , rev);
    int root = 0;
    for (int i = 0 ; i < 32 ; ++i){
//            OT(dig[i]);
        if (tree[root].son[dig[i]] == -1) root = tree[root].son[(dig[i] ^ 1)];
        else root = tree[root].son[dig[i]];
    }
    return x ^ tree[root].value;
}
void solve(){
    id = 0;
    tree[0].reset();
    Rush{
        RS(op);
        if (op[0] == 'i') insert(RD());
        else OT(query(RD() , op[2] == 'a'));
    }
}
void init(){
    REP(i , 32) mm[i] = 1 << i;
}
int main(){
    init();
    Rush solve();
}

这题暴力是绝对不可能通过的,我跑了 5.5 h。。。。


CF #173 E

这题的数据结构和上面的一样只不过建立不太相同

1、首先 1 ~ n 枚举 前 i 个数的异或值

2、将树情况,插入 0

3、从 n -> 1 枚举,每次i前缀异或值 在树中查找 max , 记录 ; 然后将后缀异或值 插入树,并且将后缀异或值 和 max 进行比较。

那么一趟下来之后,也就是枚举了每个前缀(包括空)对于不重叠后缀们的异或最大值。

代码请看

http://codeforces.com/contest/282/submission/3311133

同心树

这题也给CF #172 Div 1 A 了。。。竟然被吐槽了。。。

题目:

求两个正方形旋转的面积交
\CF 矩形OOXX

解法:

1、裸半平面交 【【【【这个没必要说吧 - - 。 其实我出这个题目的本意是用半平面交来测试数据,E 这个题目半平面交是肯定超时的。
2、计算四个三角形的面积剪掉。

Code

CF :

http://codeforces.com/contest/280/submission/3277817

本题

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const double pi=acos(-1);
int t,l,a;
double x,b;

int main(){
  scanf("%d",&t);
  while (t--){
    scanf("%d%d",&l,&a);
    b=(90-a%90)*pi/180;
    x=l/(1+sin(b)+cos(b));
    //xsinb+x+xcosb=l
    printf("%.2f\n",l*l-x*x*sin(2*b));
  }
  return 0;
}


完美数

题目叙述

求[l , r] 中 含 3 || 含 8 - (含 3 && 含 8) 的数字个数。

解法:

首先[l , r] = [1 , r] - [1 , l - 1]。因此只要能计算 [ 1 , x ] 即可
怎么算呢,这明显是个数位dp,加上状态压缩 —— (0 无3无8 , 1 有3无8 , 2 无3有8 , 3 有3有8)
于是就是个裸的数位dp了。主要看dfs函数,从高位枚举到第i位,状态是s,e代表前i位是否“填满”(比如1234 从千位枚举到各位,枚举到十位时如果是11xy形式那么e = false 如果 是 12xy 形式那么e是True)。如果e的话那么本位最高能枚举到 num[i] 否则能枚举到 9

Code

int f[100][4] , num[100];
int new_s(int s , int d){
    if (d == 3) return s | 1;
    if (d == 8) return s | 2;
    return s;
}
int dfs(int i, int s, bool e) {
    if (i==-1) return s==1 || s == 2;
    if (!e && ~f[i][s]) return f[i][s];
    int res = 0;
    int u = e?num[i]:9;
    for (int d = 0; d <= u; ++d)
        res += dfs(i-1, new_s(s, d), e&&d==u);
    return e?res:f[i][s]=res;
}
int gao(int x){
    int m = 0;
    while(x){
        num[ m++ ] = x % 10;
        x /= 10;
    }
    return dfs(m - 1 , 0 , 1);
}
void solve(){
    int l , r;
    RD(l , r);
    OT(gao(r) - gao(l - 1));
}
int main(){
//    freopen("0.in" , "r" , stdin);
//    freopen("0.out" , "w" , stdout);
    FLC(f , -1);
    Rush solve();
}

另:



这个题目少年要是写的暴力就太naive了。。


Board:



http://www.acdream.net/contestrank.php?cid=1020


总结:


祝大家玩儿的开心。。
另:
对不起 xlk , 我题目的叙述有很多歧义,感谢您能提出意见并让我耐心改正。


























































  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值