14届浙江省acm总结

由于省赛后学校就开始了招生周放假了,放假期间一起在外面玩也没怎么碰电脑,所以总结就拖到了现在。

时间过的太快了,转眼已经大二了,再过两年就毕业了,剩下给自己的时间也没有多少了。

回想去年,同队的两个大一队友一个已经不搞竞赛了,另外一个成了ctf的dalao,只剩下自己还在苦苦挣扎,挣扎了一年还是打了个铜。。。

不管是去年还是今年的省赛都是感觉再稍微前进一点就能上升一个档次,去年是差在计算循环节,今年是E题比赛时没发挥好。

今年的省赛难度递增,前四题都是水题,大概有将近180支队伍写出来吧,我们队由于大四的键叔读题比较快再加上水题我平时敲的比较多,67min4题都是1A过的,但是最后也止步于此了,挂机了将近4h哭。A完4题我们大概排在28名,但是最终排在了将近60名,当时是键叔先看了E告诉我题意和大概解法,然后我琢磨了一会感觉不会,后来在纸上写了几下大概知道了规律,然后告诉了键叔,然后我先敲了一会,后来莫名奇妙就让键叔敲了。。然后我在一旁看他敲,但是我比较急,看他敲的这么慢就自己上了,到这里就大概花了2h了,后来我把大致的代码写好了一运行,连样例都过不了,赛后重写时觉得应该是细节没处理好。就这样我让键叔继续上去写F,然后接下来的2h就是我们俩交替的调试,不过最终我也没写出来。。然后比赛最后一分钟键叔貌似写好了然后提交了一发T了,后来把输出cout改成printf再提交就已经超出比赛时间5s了。。。当时赛后我说要是能过就让他背锅哭,不过最后好像还是错的。。就这样我们两个人分开写两题都没写出来。

赛后键叔重新写了F貌似用set瞎搞就过了,我再用比赛时的方法一写也过了E,E赛后我看网上都说是数位dp,不过我用的方法貌似不是数位dp而是找规律。

E题大致方法如下:

设输入为长整型 n 与字符串 s

然后将字符串s转化成长整型m


设有函数f(x)为计算[0,x)共需要多少能量

所以最终的答案就为f(n+m)-f(m)


f(x)的计算方法大致如下:

将x转化成八位16进制数: X7X6X5X4X3X2X1X0

然后从高位开始进行计算,从高位开始计算是有原因的,

因为可以将一个8位16进制数分解成

X7+X6+X5+X4+X3+X2+X1+X0

然后当要计算f(tmp)

可找规律:

当tmp为(00000010)16 比k低位的数中0~F16个数都被计算了1次(0~F)

当tmp为(00000100)16 比k低位的数中0~F16个数都被计算了32次(00~FF)

当tmp为(00001000)16 比k低位的数中0~F16个数都被计算了768次(000~FFF)(这个不用手写可以得到规律的。。)

找规律的方法的话我是从3进制数开始考虑的然后再将进制数取大判断下可不可以,最后就得到了规律:

16进制数中0~F16个数共被计算了 (k*16^k)/16 次,分子表示一共有几数 = k位*16^k个16进制数,分母16是平分给16个数

当第k位不为1的时候也有类似的规律,比如说k位为num[k]则低位数0~F被计算次数位num[k]*(k*16^k)/16,因为0~F 16个数消耗的能量和都是固定的,所以计算第k位时低位消耗的能量和为sum*num[k]*(k*16^k)/16

因为低位变化时可能会导致进位,如 20 就是 2个0~F和16个0与16个1组成的6,这个计算比较简单就不讲了,

然后高位是不变的,把比k高的位求个和在乘一下计算次数就可以了。

最后第k位需要的能量为 sumN*16^k + sumU*num[k]*16^k + k*16^(k-1)*sum*num[k]

(num[k]为第k为数,sumN为[0,num[k])的耗能和,sumU为比k高位的数的耗能和,sum为0~F的耗能和)


还有一点要注意:因为给出的可能为 10 FFFFFFFF 这种会进位的所以要将数转化成9位16进制数来写,再考虑一些细节就可以了


代码如下:

#include<bits/stdc++.h>

using namespace std;

map<char, long long>mp;
long long d[16];
long long sum = 0;
long long pow16[10];
void init()
{
    for(long long i=0; i<10; i++)
        mp['0'+i] = i;
    for(long long i=0; i<6; i++)
        mp['A'+i] = 10 + i;
    d[0] = 6; d[1] = 2; d[2] = 5; d[3] = 5; d[4] = 4;
    d[5] = 5; d[6] = 6; d[7] = 3; d[8] = 7; d[9] = 6;
    d[10] = 6; d[11] = 5; d[12] = 4; d[13] = 5; d[14] = 5;
    d[15] = 4;
    for(int i=0; i<16; i++)
        sum += d[i];
    pow16[0] = 1;
    for(int i=1; i<10; i++)
        pow16[i] = pow16[i-1]*16;
}
long long str2long(char *s)
{
    long long ans = 0;
    for(int i=0; i<8; i++) {
        ans = ans*16 + mp[s[i]];
    }
    return ans;
}
long long calc(long long n)
{
    long long num[10];
    for(int i=0; i<9; i++) {
        num[i] = n%16;
        n /= 16;
    }
    long long ans = 0;
    long long pre = 0;
    if(num[8])
        ans += 8*pow16[7]*sum;
    for(long long i=7; i>=0; i--) {
        long long tmp = 0;
        for(int j=0; j<num[i]; j++) {
            tmp += d[j];
        }
        ans += tmp*pow16[i] + pre*num[i]*pow16[i];
        if(i)
            ans += i*pow16[i-1]*sum*num[i];
        pre += d[num[i]];
    }
    return ans;
}
int main()
{
    init();
    int T;
    cin >> T;
    while(T--) {
        long long n;
        char s[10];
        scanf("%lld %s", &n ,s);
        long long m = str2long(s);
        n += m;
        long long disn = calc(n);
        long long dism = calc(m);
        printf("%lld\n", disn-dism);
    };
    return 0;
}

/*
5
1 89ABCDEF
2 89ABCDEF
3 89ABCDEF
4 89ABCDEF
5 89ABCDEF
*/

接下来为区域赛做准备 奋斗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值