【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<bits/stdc++.h>
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 <class t>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<i&&lim<a[i]-a[j]) sum-=a[j++];
        cnt+=i-j,ans+=(ll)a[i]*(i-j)-sum;
        sum+=a[i];
	}
	return cnt;
}

int main(){
	freopen("T1.txt","r",stdin);
	//freopen("and.out","w",stdout);
	rd(n),rd(k);
	for(int i=1;i<=n;++i) rd(a[i]);
	sort(a+1,a+n+1);
	int l=0,r=a[n]-a[1],mid;
	while(l<r){
		mid=l+r>>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<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<cmath>
#include<algorithm>
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 <class t>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]<dis[u]) u=j;
		vis[u]=1,sum+=dis[u];
		for(int v=1;v<=n;++v)
		if(!vis[v]) dis[v]=Min(dis[v],d[u][v]);
	}
	return sum>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
",l);
	}
	return 0;
}

位运算

==到处都是锅

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

就是拆开成两位 ==

#include<bits/stdc++.h>
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 <class t>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<x) return;
	if(x<=l&&r<=y){
		for(int i=0;i<18;++i)
		if(!K[i]) t[o].val[i]=0,t[o].tg[i]=1;
		return;
	}
	pudw(o,l,r);
	int mid=l+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<x) return;
	if(x<=l&&r<=y){
		for(int i=0;i<18;++i)
		if(K[i]) t[o].val[i]=(r-l+1)&1,t[o].tg[i]=2;
		return;
	}
	pudw(o,l,r);
	int mid=l+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<x) return;
	if(x<=l&&r<=y){
		for(int i=0;i<18;++i) ans[i]^=t[o].val[i];
		return;
	}
	pudw(o,l,r);
	int mid=l+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);
	printf("%d
",Ans);
}
void build(int o,int l,int r){
	if(l==r){
		for(int i=0;i<18;++i) t[o].val[i]=(a[l]>>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

  • 模板不够熟练 ==居然现场推分数规划
  • 不会融会贯通 前面都考了这么多位运算了为什么不长点记性!!! 从位运算的性质去分析
原文地址:https://www.cnblogs.com/lxyyyy/p/11478141.html