【2019.9.6】

9.6

最小得分和

\(\{a_N,a_N\}\)从小到大排序。换个思路想,我们找这样一个数:在这些两两数之差的绝对值组成的序列中从这个数开始,就已经有大于等于K个数小于等于这个数。

于是自然想到要转化为判定性问题,即二分这K对数里差值最大的数相差多少,判断是否有大于等于K对满足条件。因为找有多少对满足条件又需要对于所有i二分满足条件的区间,所以时间复杂度为\(O(NlogNlogaN)\)

其实我们无需再二分有多少对满足条件的,因为\(\{ l a_N,a_N\}\)都排完序了,对于特定的差值最大的数,如果你从小到大枚举\(a_i\),其满足条件的数的区间必定是单调不下降的,所以用两个指针扫一遍就可以了(实际上只要1个,因为当前你只要统计它左边满足的数的个数),时间复杂度为\(O(NlogN+NlogaN)\),因为常数很小,可以过所有数据。

至于OKlogN)的做法运用的是堆,首先将每相邻两个元素差值入堆,每次选取当前堆中最小的数,找到它在原序列中的位置i,然后向左找到第一个没有取的数对(ip[i]),将堆顶元素变为\(s[i]-s[p[i]-1],s[i]\)为前缀和,然后\(dec(p[i])\),做K次得到答案。

还有细节需要考虑,请自行思考。

#include
using namespace std;
#define ll long long
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
#define Abs(x) ((x)<0?-(x):(x))
#define ls (o<<1)
#define rs (o<<1|1)
const int N=1000006+10,M=1e5+50,inf=0x3f3f3f3f;
int n,a[N];
ll k,ans;
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

ll check(int lim){
    ll sum=0,cnt=0;ans=0;
    for(int i=1,j=1;i<=n;++i){
        while(j>1;
        if(check(mid)>=k) r=mid;
        else l=mid+1;
    }
    ll kk=check(r-1);
    ans+=r*(k-kk);
    printf("%lld",ans);
    return 0;
}

总统竞选

==就是最优比率生成树的模板

r不应该是10的 只是懒得搞那个迭代的那个版本了

#include
#include
#include
#include
#include
#include
using namespace std;
#define ll long long
#define Abs(x) ((x)<0?-(x):(x))
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
const int N=1000+5,M=5e5+5,INF=1e9+7,inf=0x3f3f3f3f;
const double eps=1e-5;
int n,k;
double a[N][N],b[N][N];
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

struct cou{int x,y,z;}c[N];
double qdis(int x,int y){return sqrt((double)(1.0*x*x)+(1.0*y*y));}

bool vis[N];
double sum,dis[N],d[N][N];
bool check(double mid){
    for(int i=0;i<=n;++i) dis[i]=1e20,d[i][i]=0.0,vis[i]=0;
    dis[1]=sum=0.0;
    for(int i=1;i<=n;++i)
    for(int j=i+1;j<=n;++j)
    d[i][j]=d[j][i]=b[i][j]-mid*a[i][j];
    for(int i=1,u=0;i<=n;++i,u=0){
        for(int j=1;j<=n;++j)
        if(!vis[j]&&dis[j]0;
}

int main(){
    //freopen("build.in","r",stdin);
//  freopen("build.out","w",stdout);
    while(scanf("%d",&n)!=EOF&&n){
        for(int i=1;i<=n;++i) rd(c[i].x),rd(c[i].y),rd(c[i].z);
        double l=0.0,r=10.0,mid;
        for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
        a[i][j]=a[j][i]=qdis(c[i].x-c[j].x,c[i].y-c[j].y),b[i][j]=b[j][i]=(double)Abs(c[i].z-c[j].z);//,r=Max(r,b[i][j]/a[i][j])
        while(r-l>=eps){
            mid=(l+r)/2;
            if(check(mid)) l=mid;
            else r=mid;
        }
        printf("%.3f\n",l);
    }
    return 0;
}

位运算

==到处都是锅

初步想法:因为只有区间的元素修改与查询操作,很容易想到线段树。
因为混合运算时不满足交换率,导致不能直接使用lazy进行标记。
先从特殊情况考虑:如果所有数均为0或1,且K为0或1。
于是只要简单的lazy标记即可。
由于位运算时二进制不同位互不影响,将每一位分开做,再将每一位的答案合并,即可得到最终答案。
时间复杂度\(O(Mloga_nlogN)\)

就是拆开成两位 ==

#include
using namespace std;
#define ll long long
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
#define Abs(x) ((x)<0?-(x):(x))
#define ls (o<<1)
#define rs (o<<1|1)
const int N=200000+10,M=1e5+50,inf=0x3f3f3f3f;
int n,m,a[N],K[20];
template void rd(t &x){
    x=0;int w=0;char ch=0;
    while(!isdigit(ch)) w|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=w?-x:x;
}

struct node{int tg[19];bool val[19];}t[N<<2];
void pup(int o){
    for(int i=0;i<18;++i) t[o].val[i]=t[ls].val[i]^t[rs].val[i];
}
void pudw(int o,int l,int r){
    int mid=l+r>>1;
    for(int i=0;i<18;++i)
    if(t[o].tg[i]){
        if(t[o].tg[i]==1) t[ls].val[i]=t[rs].val[i]=0;
        else t[ls].val[i]=(mid-l+1)&1,t[rs].val[i]=(r-mid)&1;//区间赋为1 异或和:奇为1 偶为0 
        t[ls].tg[i]=t[rs].tg[i]=t[o].tg[i];
        t[o].tg[i]=0;
    }
}

void And(int o,int l,int r,int x,int y){
    if(l>y||r>1;
    And(ls,l,mid,x,y),And(rs,mid+1,r,x,y);
    pup(o);
}
void Or(int o,int l,int r,int x,int y){
    if(l>y||r>1;
    Or(ls,l,mid,x,y),Or(rs,mid+1,r,x,y);
    pup(o);
}

int Ans,ans[20];
void Xor(int o,int l,int r,int x,int y){
    if(l>y||r>1;
    Xor(ls,l,mid,x,y),Xor(rs,mid+1,r,x,y);
    pup(o);
}
void print(int l,int r){
    Ans=0;
    memset(ans,0,sizeof(ans));
    Xor(1,1,n,l,r);
    for(int i=0;i<18;++i)
    if(ans[i]) Ans|=(1<>i)&1;
        return;
    }
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
    pup(o);
}

int main(){
    freopen("T3.txt","r",stdin);
    //freopen("and.out","w",stdout);
    rd(n),rd(m);
    for(int i=1;i<=n;++i) rd(a[i]);
    build(1,1,n);
    for(int i=1,opt,x,y,k;i<=m;++i){
        rd(opt);
        if(opt==3) rd(x),rd(y),print(x,y);
        else if(opt==1||opt==2){
            rd(x),rd(y),rd(k);
            for(int i=0;i<18;++i) K[i]=(k>>i)&1;
            if(opt==1) And(1,1,n,x,y);
            else if(opt==2) Or(1,1,n,x,y);
        }
    }
    return 0;
}

summary

  • 模板不够熟练 ==居然现场推分数规划
  • 不会融会贯通 前面都考了这么多位运算了为什么不长点记性!!! 从位运算的性质去分析

你可能感兴趣的:(【2019.9.6】)