20171030 考试总结

考试策略

T1 和异或有关…… 异或的东西我都不是很懂,估计做不出来,打个 20 分暴力。

T2 是数学题…… 数学我完全学不懂啊,估计做不出来,打个暴力……woc 为什么模数这么小,取逆元都不好搞。

T3 是什么鬼啦,不过 50 分暴力还是会的…… 为什么死活调不出来啊喂!

今天考试体验极差….. 姑且不论机房好冷和键盘特别反人类,出题人也很反人类而且 jyf 和 boshi 还会在做出题后敲键盘欢呼是什么鬼啦。

反正就是炸了……

T1

题目分析

首先假设所有数在二进制下有 num(i) 个数第 i 位是 1,那么这一位造成的异或和贡献是 $2\times (n-num(i))\times num(i)$嘛。所以用一个 map 来搞所有数,还是不难的…..

可是出题人太反人类了,模数给得特别大,所以必须用快速乘……

可是如果用 log 复杂度的快速乘会被卡成暴力分……

所以只能用 O(1) 玄学快速乘,并且取模还要玄学一发…..

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
#define LL unsigned long long
LL read() {
    LL q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
    return q;
}
const int N=200005;
map<LL,int>mp;
int n,m;LL p;
LL a[N],bin[63],ans,num[63];
inline LL qm(LL x){return x>=p?x-p:x;}
LL mul(LL x,LL y){
    return (x*y-(LL)((long double)x/p*y)*p+p)%p;
}
LL ask() {
    LL re=0;
    for(int i=0;i<=62;++i)
        re+=mul(bin[i],mul(num[i],n-num[i])),re=qm(re);
    return re;
}
void work(LL x,LL y) {
    if(!mp[x]) return;
    int kl=mp[x];mp[y]+=mp[x],mp[x]=0;
    for(int i=0;i<=62;++i) {
        if((x&bin[i])) num[i]-=kl;
        if((y&bin[i])) num[i]+=kl;
    }
}
int main()
{
    freopen("meaningless.in","r",stdin);
    freopen("meaningless.out","w",stdout);
    int bj,i,j;LL x,y;
    n=read(),m=read();
    bin[0]=1;for(i=1;i<=62;++i) bin[i]=bin[i-1]<<1;
    for(i=1;i<=n;++i) {
        a[i]=read(),++mp[a[i]];
        for(j=0;j<=62;++j) if((a[i]&bin[j])) ++num[j];
    }
    for(i=1;i<=m;++i) {
        bj=read();
        if(bj==1) x=read(),y=read(),work(x,y);
        else p=read(),printf("%lld\n",qm(ask()<<1));
    }
    return 0;
}

T2

题目分析:

p=2333

设 $S_n^k=\sum^k_{i=0} C_n^i \bmod p$

则利用卢卡斯定理

$S_n^k=\sum^k _ {i=0}(C^{n \bmod p} _ {n \bmod p}\times C^{i/p} _ {n/p})$

然后通过考虑 $i/p$的取值来提取公因数(注意最后一项要单独考虑……)

$= \sum_{i=0}^{k/p-1}(C_{n/p}^i * \sum_{j=0}^{p-1} C_{n \bmod p}^j)+ C_{n/p}^{k/p}*\sum_{i=0}^{k \bmod p} C^i_{n \bmod p}$

然后前面的式子提取一下公因式,用 S 的定义化简一下:

$=S_{n/p}^{k/p-1}\times S_{n \bmod p}^{p-1}+C_{n/p}^{k/p}\times S_{n \bmod p}^{k \bmod p}$

于是我们可以打表出 n 与 k 小于 p 的所有 S 和 C,然后运用递归和卢卡斯定理的手法来求解。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
LL read() {
    LL q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
    return q;
}
int T,mod=2333;
LL n,k,c[2340][2340],s[2340][2340];
inline int qm(int x){return x>=mod?x-mod:x;}
void init() {
    int i,j;
    for(i=0;i<mod;++i) {
        c[i][0]=1;
        for(j=1;j<=i;++j) c[i][j]=qm(c[i-1][j]+c[i-1][j-1]);
    }
    for(i=0;i<mod;++i) {
        s[i][0]=1;
        for(j=1;j<mod;++j) s[i][j]=qm(s[i][j-1]+c[i][j]);
    }
}
int lucas(LL n,LL k) {
    if(k>n) return 0;
    if(n<mod&&k<mod) return c[n][k];
    return lucas(n/mod,k/mod)*c[n%mod][k%mod]%mod;
}
int work(LL n,LL k) {
    if(n<mod&&k<mod) return s[n][k];
    int re=lucas(n/mod,k/mod)*s[n%mod][k%mod]%mod;
    return qm(work(n/mod,k/mod-1)*s[n%mod][mod-1]%mod+re);
}
int main()
{
    freopen("quondam.in","r",stdin);
    freopen("quondam.out","w",stdout);
    T=read();init();
    while(T--) n=read(),k=read(),printf("%d\n",work(n,k));
    return 0;
}

T3

题目分析:

考虑枚举剩余血量,那么不能用了的攻击可以强制用掉,然后玩多重背包。

那么我们可以根据攻击伤害来枚举剩余血量。

其实还可以排序后一边算多重背包一边计算答案。

然后用队列优化(计数问题无需单调队列)一下多重背包即可。

注意假如从大到小排序后,剩余血量应大于等于 w(i), 那么要强制用掉前 i-1 个技能,并且第 i 个技能要留下一个,跑多重背包。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
const int N=505,M=100005;
int n,m,f[2][M],tot,t,ans,mod=19260817;
struct node{int num,w;}a[N];
bool cmp(node x,node y) {return x.w<y.w;}
inline int qm(int x){return x>=mod?x-mod:x;}
void work(int num,int w) {
    int c,j,he,sum;
    for(c=0;c<w;++c) {
        he=sum=0;
        for(j=0;j<=(m-c)/w;++j) {
            sum=qm(sum+f[t^1][j*w+c]);
            //直接计算所有能达成的情况,用队列排除不能达成的即可
            while(j-he>num) sum=qm(sum-f[t^1][w*he+c]+mod),++he;
            f[t][j*w+c]=sum;
        }
    }
}
int main()
{
    freopen("refrigerator.in","r",stdin);
    freopen("refrigerator.out","w",stdout);
    int i,j;
    n=read(),m=read(),--m;
    for(i=1;i<=n;++i)
        a[i].num=read(),a[i].w=read(),tot+=a[i].num*a[i].w;
    if(tot<=m) {puts("1");return 0;}
    sort(a+1,a+1+n,cmp);
    f[0][0]=1;
    for(i=n;i>=1;--i) {
        tot-=a[i].num*a[i].w,t^=1;
        work(a[i].num-1,a[i].w);
        for(j=max(0,m-tot-a[i].w+1);j<=m-tot;++j) ans=qm(ans+f[t][j]);
        work(a[i].num,a[i].w);//重新计算
    }
    printf("%d\n",ans);
    return 0;
}
分类: 文章

litble

苟...苟活者在淡红的血色中,会依稀看见微茫的希望

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注