7.6

7.6

Binary

Description

img

Solution

遇到位运算直接拆位。

都拆了的话二进制 + 操作不好处理,所以只拆y

1594037129430

​ 对于每个二进制位单独考虑,且y这一位为1才考虑。
​ 不考虑+x时,我们还可以弄一棵权值线段树,那么cnt[i]所包含的数就是这样的:比i高的位任意,i位以内满足在$$[ 2^{i−1}, 2^i −1 ]$$这个区间内。可以发现我们查询的区间对于整个值域来说并不连续,而是一段一段的,因此我们对每个二进制位都开一棵值域线段树,第i位的线段树存储的数则由ai变为ai mod $$2^i$$。这样我们操作第i位时,直接在第i位的线段树中查询$$[ 2^{i−1}, 2^i −1 ]$$内的数有多少个,就行了。
​ 接下来考虑+x。这个实际上是对查询区间的位移,比如当前查询区间$$[ 2^{i−1}, 2^i −1 ]$$内,那么就变成$$[ 2^{i−1}-x, 2^i −1-x ]$$内。要注意这里的x是mod $$2i$$的。唯一的问题就是区间左端点可能为负。这好办,先把0到右端点的正常操作,假设左端点变成了−z,那我们再查询$$[2i−z,2^i−1]$$就行了。这里相当于是退位一样的东西

把线段树换成树状数组,20个线段树爆空间

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lowbit(x) (x&(-x))
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m,a[100005],t[25][1<<20];
inline void update(int x,int y,int pos){
	while(x<=(1<<(pos+1))){
		t[pos][x]+=y;
		x+=lowbit(x);
	}
}
inline int query(int x,int pos){
	int sum=0;
	while(x)
		sum+=t[pos][x],x-=lowbit(x);
	return sum;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		for(int j=0;j<=19;j++)
			update(a[i]%(1<<(j+1))+1,1,j);// lowbit(0)是死循环,所以+1
			//拆位 
	}
	for(int i=1;i<=m;i++){
		int op=read(),x=read(),y=read();
		if(op==1){
			for(int j=0;j<=19;j++)
				update(y%(1<<(j+1))+1,1,j),//加上y的贡献
				update(a[x]%(1<<(j+1))+1,-1,j);//撤掉x的贡献
			a[x]=y;
		}else{
			long long ans=0ll;
			for(int j=0;j<=19;j++){
				if((y&(1<<j))==0)continue;
				int l=(1<<j)-1,r=(1<<(j+1))-1;
				l=(l-x+(1<<20))%(1<<(j+1));
				r=(r-x+(1<<20))%(1<<(j+1));
				if(l<=r)ans+=(long long)(1<<j)*(query(r+1,j)-query(l+1,j));
				else ans+=(long long)(1<<j)*(query(r+1,j)+query((1<<(j+1))+1,j)-query(l+1,j));
			}
			printf("%lld\n",ans);
		}
	} 
	
	return 0;
} 

Value

Description

给定n个物品,每个物品价值为vi代价为wi。

可以以任意顺序选择任意数量的物品,但在选择i号物品以后,剩下物品的价值就会减少wi,要求最大化选择商品的价值之和。

\[n≤5000,vi,wi≤105 \]

Solution

考试骗分40

(n<=8) 全排列直接搞。。。

正解:(其实很简单)

首先可以看出贪心,按w[i] 从小到大排序

然后dp,设f[i] [j]表示前 i 个物品当前到第 j个的最大价值

因为选了这个东西之后会对后面的物品造成影响,所以考虑倒序枚举,这样选了 j 就对背包里已有的物品(已知状态)产生影响了

然后这时候w[i] 从大到小排序,循环还是正着写

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N=5005;
int n,f[N][N],ans=-0x3f3f3f3f;
struct node{
	int v,w;
}a[N];
bool cmp(node a,node b){
	return a.w>b.w;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		a[i].v=read(),a[i].w=read();
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++)
			f[i&1][j]=max(f[(i-1)&1][j],f[(i-1)&1][j-1]+a[i].v-(j-1)*a[i].w);
	for(int i=1;i<=n;i++)
		ans=max(ans,f[n&1][i]) ;
	printf("%d\n",ans);
	return 0;
} 

matrix

Description

1594090019736

1594090079114

点(x1,y1)对(x2,y2)的贡献为

\[a^{x2-x1}*b^{y2-y1}* (x1,y1,x2,y2)路径数 \]

(0,0)到(n,m)的路径数为C(n+m,m)( 只往右或往下走)

从最左一边走到最右一边,需要走n-1步,从最上边走到最下边,需要走m-1步,所以一共走n+m-2步。这n+m-2步中,有些步是向右的,有些步是向下的,将这些步排列组合ans=c(n+m-2,n-1)或ans=c(n+m-2,m-1)

看样例,我们列出这样的东西(假设a,b为任意常数):

UPHLnK.png

把矩阵翻转成杨辉三角,然后数一数要求的点在第几行,第几列,然后根据杨辉三角第i行,第j列的值为 c(i,j) 得出

#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
#define p 1000000007
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const int N = 200005; 
LL n,a,b;
LL fac[N],inv[N];
LL qsm(LL a,LL b){
	LL res=1;
	while(b){
		if(b&1)res=(res*a)%p;
		b>>=1;
		a=(a*a)%p;
	}
	return res;
}
LL C(LL n,LL m){
	if(n<m)return 0;
    return (fac[n]*inv[m]%p)*inv[n-m]%p;
}
LL x[N],y[N],ans;
int main(){
	n=read();a=read();b=read();
	for(int i=1;i<=n;i++)y[i]=read();
	for(int i=1;i<=n;i++)x[i]=read();
	fac[0]=1;
	for(int i=1;i<=2*n;i++)fac[i]=(i*fac[i-1])%p;
	inv[2*n]=qsm(fac[2*n],p-2);
	for(int i=2*n-1;i>=0;i--)inv[i]=(inv[i+1]*(i+1))%p;
	a%=p,b%=p;
	for(int i=2;i<=n;i++){
		int tmp=x[i]%p;
		tmp=(tmp*qsm(a,n-i))%p;
		tmp=(tmp*qsm(b,n-1))%p;
		tmp=(tmp*C(2*n-2-i,n-2))%p;//第一行只能向下,第一列只能向右 
		ans=(ans+tmp)%p;
		tmp=y[i]%p;
		tmp=(tmp*qsm(a,n-1))%p;
		tmp=(tmp*qsm(b,n-i))%p;
		tmp=(tmp*C(2*n-2-i,n-2))%p;
		ans=(ans+tmp)%p;
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2020-08-14 00:09  ke_xin  阅读(241)  评论(0编辑  收藏  举报