省选测试39

老洪的遍历

考场上直接spfa+拉斯维加斯剪枝拿到了 \(n \leq 1000\) 的65分

考虑将i到j的收益的式子展开:

\[\frac{B_iB_j}{2}*\frac{A_i-A_j}{A_i*A_j} \]

\[\frac{B_iB_j}{2}*(\frac{1}{A_j}-\frac{1}{A_i}) \]

\[\frac{1}{2}*(B_i*\frac{B_j}{A_j}-B_j*\frac{B_i}{A_i}) \]

  • 我们将第i个点放到坐标轴上,坐标 \((B_i,\frac{B_i}{A_i})\),发现i到j的贡献就是叉积形式,又带个 \(\frac{1}{2}\),所以就是点i,j和原点形成的有向三角形面积,且根据题目给出的限制,发现由i开始,回到i的最大收益是i所在的一个多边形的面积

  • 然后因为起点任意,并且这个最大面积和起点并没有关系,我们只要找到这些点集的凸包,算它的面积就行了。

Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define double long double
using namespace std;

const int maxn=1e5+10;
const double eps=1e-12;
int n,top;
struct Node{
	double x,y;
	Node operator - (const Node &B) const {return (Node){x-B.x,y-B.y};}
	double operator ^ (const Node &B) const {return x*B.y-B.x*y;}
	double Dis(const Node &B){return sqrt((x-B.x)*(x-B.x)+(y-B.y)*(y-B.y));}
}a[maxn],sta[maxn];

bool cmp(Node A,Node B){
	double tmp=((A-a[1])^(B-a[1]));
	return (fabs(tmp)>eps)?(tmp>eps):(A.Dis(a[1])<B.Dis(a[1]));
}

int main(){
	freopen("visit.in","r",stdin);
	freopen("visit.out","w",stdout);
	scanf("%d",&n);
	double sum=0;
	for(int i=1;i<=n;++i){
		double aa;scanf("%Lf",&aa);
		sum+=aa;
		a[i]=(Node){sum,sum/aa};
		if(a[i].y<a[1].y || (a[i].y==a[1].y&&a[i].x<a[1].x)) swap(a[1],a[i]);
	}
	sort(a+2,a+1+n,cmp);
	sta[++top]=a[1];
	for(int i=2;i<=n;++i){
		while(top>1&&((sta[top]-sta[top-1])^(a[i]-sta[top-1]))<=eps) --top;
		sta[++top]=a[i];
	}
	sta[top+1]=a[1];
	double ans=0;
	for(int i=1;i<=top;++i) ans+=(sta[i]^sta[i+1]);
	printf("%.5Lf\n",ans/2);
	return 0;
}

老洪的神秘操作

  • 将a[i]处理一下,在模7意义下取反(满足a[i]模7为0需要加的数),然后处理出a数组的差分数组c,这样我们对a数组区间减就只需要改c数组两个位置了,

  • 显然我们要将c[1]~c[n]全变为0,有x个不为0,其实最劣可以操作x次达到目的,因为操作一次是一减一加,如果我们能同时抵消掉2个,结果会更优,

  • 我们把差分数组中的负数+7变成正数,所以我们要把差分数组中的所有数按加和为7分组,每组操作次数为siz-1,我们要让这种组合出来的组数尽量多

  • 我们先将贪心地将1和6,2和5,3和4两两配对,剩下的只有3种数,动态规划求出这些数能配出来的最大组数,f[i][j][k][w]表示数1用了i个,数2用了j个,数3用了k个,最后一组加和模7为w,前面配好的加和为7的最大组数,然后滚动数组优化掉x就好了

Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int n,ans,a[505],c[505],cnt[10],v[10];
int f[2][505][505][8];

int main(){
	freopen("secret.in","r",stdin);
	freopen("secret.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]),a[i]=(7-a[i])%7;
	for(int i=1;i<=n;++i) c[i]=a[i]-a[i-1],c[i]=(c[i]+7)%7;
	for(int i=1;i<=n;++i) ++cnt[c[i]];
	for(int i=1;i<=3;++i){
		ans+=min(cnt[i],cnt[7-i]);
		if(cnt[i]<cnt[7-i]) cnt[i]=cnt[7-i]-cnt[i],v[i]=7-i;
		else cnt[i]-=cnt[7-i],v[i]=i;
	}
	for(int i=0;i<=cnt[1];++i){
		int now=i&1;
		memset(f[now],-0x3f,sizeof(f[now]));
		if(!i) f[0][0][0][0]=0;
		for(int j=0;j<=cnt[2];++j){
			for(int k=0;k<=cnt[3];++k){
				for(int w=0;w<7;++w){
					if(i) f[now][j][k][w]=max(f[now][j][k][w],f[now^1][j][k][(w+7-v[1])%7]+(!w));
					if(j) f[now][j][k][w]=max(f[now][j][k][w],f[now][j-1][k][(w+7-v[2])%7]+(!w));
					if(k) f[now][j][k][w]=max(f[now][j][k][w],f[now][j][k-1][(w+7-v[3])%7]+(!w));
				}
			}
		}
	}
	int tot=0;
	for(int i=0;i<7;++i) tot=max(tot,f[cnt[1]&1][cnt[2]][cnt[3]][i]);
	printf("%d\n",ans+cnt[1]+cnt[2]+cnt[3]-tot);
	return 0;
}

老洪的数组

分块

将询问分块,固定kc次询问后重新处理出20×n的表,对于询问,首先设为表上的值,然后暴力枚举块内的改过的点,加上差值(变化量),假设m次操作中,修改和询问各占一半,那么块长 \(\sqrt{80*n}\) 最优,时间复杂度为 \(O(m\sqrt{20n})\)

Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;

const int maxn=1e5+30;
const int mod=1e9+7;
int n,m,kc,cnt;
int fac[maxn],fy[maxn],v[maxn],p[maxn],f[21][maxn];
bool vis[maxn];

int read(int x=0,bool f=0,char ch=getchar()){
	for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
	return f?-x:x;
}

int fp(int a,int x,int ans=1){
	for(;x;x>>=1,a=(ll)a*a%mod)
		if(x&1) ans=(ll)ans*a%mod;
	return ans;
}

void Init(int n){
	fac[0]=fy[0]=1;
	for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod;
	fy[n]=fp(fac[n],mod-2);
	for(int i=n;i>1;--i) fy[i-1]=(ll)fy[i]*i%mod;
}

int C(int x,int y){
	return (x<y || y<0)?0:(ll)fac[x]*fy[y]%mod*fy[x-y]%mod;
}

void rebuild(){
	for(int i=1;i<=cnt;++i) f[0][p[i]]=v[p[i]],vis[p[i]]=0;
	cnt=0;
	for(int i=1;i<=20;++i) for(int j=1;j<=n;++j) f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
}

int main(){
	freopen("array.in","r",stdin);
	freopen("array.out","w",stdout);
	n=read(),m=read(),kc=(int)sqrt(80*n);
	for(int i=1;i<=n;++i) f[0][i]=read();
	Init(n+20);
	for(int i=1;i<=m;++i){
		if(i%kc==1) rebuild();
		int opt=read(),x=read(),y=read();
		if(opt==1){
			if(!vis[x]) vis[x]=1,p[++cnt]=x;
			v[x]=y;
		}
		else{
			if(!x){printf("%d\n",vis[y]?v[y]:f[0][y]);continue;}
			ll ans=f[x][y];
			for(int j=1;j<=cnt;++j) ans+=(ll)C(x-1+y-p[j],x-1)*(v[p[j]]-f[0][p[j]])%mod;
			printf("%lld\n",(ans%mod+mod)%mod);
		}
	}
	return 0;
}
posted @ 2021-03-15 19:57  liuzhaoxu  阅读(64)  评论(0编辑  收藏  举报