叉姐的魔法训练(第一课)---- 初级魔法练习

一 集合操作

POJ 2443 Set Operation

有1000个集合每个集合有10000个元素,给出每个集合所有的元素和Q组询问,问元素x和y是否属于同一个集合。

手抽写了个集合类出来,效率低了。其实用元素开数组,压缩所属的集合效率更高。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1111;
typedef unsigned int uint;
const int Size=30;
class SetOperation{
private:
    uint st[400];
    int getIdx(int num){
        return num/Size;
    }
    int getLft(int num){
        return num%Size;
    }
public:
    SetOperation(){
        init();
    }
    void init(){
        memset(st,0,sizeof(st));
    }
    void addVal(int num){
        st[getIdx(num)]|=(1<<getLft(num));
    }
    void delVal(int num){
        st[getIdx(num)]&=~(1<<getLft(num));
    }
    void chgVal(int num){
        st[getIdx(num)]^=(1<<getLft(num));
    }
    bool inSet(int num){
        return st[getIdx(num)]&(1<<getLft(num));
    }
}a[maxn];
int main()
{
    int n,m;
    while (~scanf("%d",&n)){
        for (int i=1;i<=n;i++){
            a[i].init();
            scanf("%d",&m);
            while (m--){
                int num;
                scanf("%d",&num);
                a[i].addVal(num);
            }
        }
        scanf("%d",&m);
        while(m--){
            int x,y;
            bool flag=false;
            scanf("%d%d",&x,&y);
            for (int i=1;i<=n;i++){
                if (a[i].inSet(x)&&a[i].inSet(y)){
                    flag=true;
                    break;
                }
            }
            if (flag) printf("Yes
");
            else printf("No
");
        }
    }
    return 0;
}

---------------------------------

二 公式推导

POJ 3244 Difference between Triplets


//数学好题
//定义两个三元组I(xi,yi,zi)和J(xj,yj,zj),(可以看做是空间中的点)
//他们的距离为D(I,J)=max{xi-xj,yi-yj,zi-zj}-min{xi-xj,yi-yj,zi-zj},
//给定n个三元组(n<=200000),求任意两个三元组的差的和
//抽化出来的模型是 max(a,b,c)-min(a,b,c),这个东西吧他放在数轴上 a,b,c
//我们要求最大和最小的差就是这三个点构成的线段的距离,那么我们这里再变通下 是不是端点到中间那个点的距离
//其实画出这个图的时候,就可以看到这个距离为(|a-b|+|b-c|+|c-a|)/2,这样我们并不需要关心中间的那个
//对应到题目中的原型,就是(|(xi-xj)-(yi-yj)|+|(yi-yj)-(zi-zj)|+|(zi-zj)-(xi-xj)|)/2;
//对应到同一个点上就是(|(xi-yi)-(xj-yj)|+|(yi-zi)-(yj-zj)|+|(zi-xi)-(zj-xj)|)/2;
//设a=(xi-yi),b=(yi-zi),c=(zi-xi),原问题等价为(|ai-aj|+|bi-bj|+|ci-cj|)/2;
//然后三个可以完全分开完全独立的计算,并不影响其他两元,这里要加个优化,就是按从小到大排序出来
//我们只需要算出每个位置上,他贡献了多少次加法,贡献了多少次减法即可
//举个例子,目前把a的部分排序了,对于第i个,他前面的比它小,所以在和i点比较时i点贡献了i次加,对后面的n-i个点
//向他们贡献了n-i次减法

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=411111;
typedef long long LL;
LL a[maxn],b[maxn],c[maxn];
int n;

int main()
{
    while (~scanf("%d",&n)){
        if (n==0) break;
        for (int i=0;i<n;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            a[i]=x-y;
            b[i]=y-z;
            c[i]=z-x;
        }
        sort(a,a+n);
        sort(b,b+n);
        sort(c,c+n);
        LL ans=0;
        for (int i=0;i<n;i++){
            ans+=(a[i]+b[i]+c[i])*(2*i-n+1);
        }
        printf("%I64d
",ans/2);
    }
    return 0;
}

---------------------------------

三 嵌套二分

POJ 3685 Matrix

打表后可以发现矩阵存在单调的性质。

F(i)=i^2+100000*i+j^2-100000*j+i*j 对i求导

F'(i)=2*i+100000+j>0 恒成立,所以每一列都是单调递增的。

要求出矩阵中第K大的数。

首先二分矩阵中的数X,判断它是不是比K个以上的数大。

然后枚举每一列,对于每一列,二分求出比X小的数的个数。

累加得到矩阵中比X小的数的个数sum。若sum>=K则X比K个以上的数大。

最后得到比矩阵中K个以上的数大的数中最小的数ans,则ans-1即为答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef __int64 LL;
const LL INFF=1LL<<50;
int n;
LL m;
LL f(LL i,LL j){
    return i*i+100000*i+j*j-100000*j+i*j;
}
bool can(LL x){
    int i;
    LL sum,l,r,res,mid;
    sum=0;
    for (i=1;i<=n;i++){
        l=1,r=n;
        res=n+1;
        while (l<=r){
            mid=(l+r)>>1;
            if (f(mid,i)>=x){
                res=mid;
                r=mid-1;
            }
            else{
                l=mid+1;
            }
        }
        sum+=res-1;
    }
    return sum>=m;
}

int main()
{
    int T;
    LL l,r,ans,mid;
    scanf("%d",&T);
    while (T--){
        scanf("%d%I64d",&n,&m);
        l=-INFF,r=INFF;
        ans=0;
        while (l<=r){
            mid=(l+r)>>1;
            if (can(mid)){
                ans=mid;
                r=mid-1;
            }
            else{
                l=mid+1;
            }
        }
        printf("%I64d
",ans-1);
    }
    return 0;
}


---------------------------------


原文地址:https://www.cnblogs.com/cyendra/p/3681637.html