zoukankan      html  css  js  c++  java
  • [CLYZ2017]day10

    圆圈游戏

    image

    solution

    60分

    记每个圆的最小的包含它的那个圆为父亲,那么图就是森林.
    再用一个无穷大的圆包含它们,那么图就是一棵树.
    选了父亲就不能选孩子.

    \(f[i]\)表示以\(i\)为根的子树的最大价值.

    \(f[i]=max(w[i],\sum{f[k]}),k\)\(i\)的孩子.

    直接枚举找父亲,\(O(n^2)\).

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 100005
    using namespace std;
    typedef long long ll;
    struct circle{
    	int x,y,r,w;
    }a[N],b[N];
    struct graph{
    	int nxt,to;
    }e[N];
    int f[N],g[N],fa[N],n,cnt;
    inline int read(){
    	int ret=0;char c=getchar();
    	while(!isdigit(c))
    		c=getchar();
    	while(isdigit(c)){
    		ret=(ret<<1)+(ret<<3)+c-'0';
    		c=getchar();
    	}
    	return ret;
    }
    inline void addedge(int x,int y){
    	e[++cnt]=(graph){g[x],y};g[x]=cnt;
    }
    inline ll sqr(int k){
    	return 1ll*k*k;
    }
    inline ll dis(int i,int j){
    	return sqr(a[i].x-a[j].x)+sqr(a[i].y-a[j].y);
    }
    //j包含i 
    inline bool chk(int i,int j){
    	return a[j].r>a[i].r&&sqr(a[j].r-a[i].r)>dis(i,j);
    }
    inline void dfs(int u){
    	for(int i=g[u];i;i=e[i].nxt){
    		dfs(e[i].to);
    		f[u]+=f[e[i].to];
    	}
    	f[u]=max(f[u],a[u].w); 
    }
    inline void Aireen(){
    	n=read();
    	for(int i=1;i<=n;++i)
    		a[i]=(circle){read(),read(),read(),read()};
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=n;++j)
    			if(chk(i,j)){
    				if(!fa[i]) fa[i]=j;
    				else if(a[j].r<a[fa[i]].r) fa[i]=j;
    			}
    	for(int i=1;i<=n;++i)
    		addedge(fa[i],i);
    	for(int i=1;i<=n;++i)
    		printf("%d\n",fa[i]);
    	dfs(0);
    	printf("%d\n",f[0]);
    }
    int main(){
    	freopen("circle.in","r",stdin);
    	freopen("test.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    100分

    考虑扫描线.
    将圆按圆上最左边的点排序,将圆分为上下两半弧.
    在圆的最左点将圆插入,在圆的最右点将圆删去.
    插入一个圆之前,先找到第一个在它上面的半弧,如果是下半弧,为它的兄弟;如果是上半弧,为它的父亲.均可知它的父亲.
    \(set\)维护,\(O(nlogn)\)

    #include<set>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 100005
    #define eps 1e-13
    using namespace std;
    typedef long long ll;
    struct graph{
    	int nxt,to;
    }e[N];
    int f[N],g[N],x[N],y[N],r[N],w[N],fa[N],n,m,cx,cnt;
    struct circle{
    	int n,k;
    };
    inline ll sqr(int k){
    	return 1ll*k*k;
    }
    bool operator < (circle a,circle b){
    	double ya,yb;
    	ya=y[a.n]+sqrt(sqr(r[a.n])-sqr(cx-x[a.n]))*a.k;
    	yb=y[b.n]+sqrt(sqr(r[b.n])-sqr(cx-x[b.n]))*b.k;
    	if(fabs(ya-yb)>eps) return ya<yb;
    	return a.k<b.k;
    }
    set<circle> s;
    inline int read(){
    	int ret=0;char c=getchar();
    	while(!isdigit(c))
    		c=getchar();
    	while(isdigit(c)){
    		ret=(ret<<1)+(ret<<3)+c-'0';
    		c=getchar();
    	}
    	return ret;
    }
    inline void addedge(int x,int y){
    	e[++cnt]=(graph){g[x],y};g[x]=cnt;
    }
    inline void dfs(int u){
    	for(int i=g[u];i;i=e[i].nxt){
    		dfs(e[i].to);
    		f[u]+=f[e[i].to];
    	}
    	f[u]=max(f[u],w[u]); 
    }
    struct point{
    	int p,k,n;
    }p[N<<1];
    inline bool cmp(point a,point b){
    	if(a.p!=b.p) return a.p<b.p;
    	return a.k<b.k;
    }
    inline void Aireen(){
    	n=read();
    	for(int i=1;i<=n;++i){
    		x[i]=read();y[i]=read();
    		r[i]=read();w[i]=read();
    		p[++m]=(point){x[i]-r[i],1,i};
    		p[++m]=(point){x[i]+r[i],-1,i};
    	}
    	sort(p+1,p+1+m,cmp);
    	circle tmp1,tmp2,j;cx=p[1].p;
    	r[0]=1000000000;
    	tmp1=(circle){0,-1};
    	tmp2=(circle){0,1};
    	s.insert(tmp1);s.insert(tmp2);
    	for(int i=1;i<=m;++i){
    		cx=p[i].p;
    		tmp1=(circle){p[i].n,-1};
    		tmp2=(circle){p[i].n,1};
    		if(p[i].k<0){
    			s.erase(tmp1);s.erase(tmp2);
    		}
    		else{
    			j=(*s.lower_bound(tmp1));
    			if(j.k>0) fa[p[i].n]=j.n;
    			else fa[p[i].n]=fa[j.n];
    			s.insert(tmp1);s.insert(tmp2);
    		}
    	}
    	for(int i=1;i<=n;++i)
    		addedge(fa[i],i);
    	dfs(0);
    	printf("%d\n",f[0]);
    }
    int main(){
    	freopen("circle.in","r",stdin);
    	freopen("circle.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    划分序列

    image

    solution

    60分

    二分答案\(ans\).

    元素均为非负数

    贪心判断即可.

    nK不超过几千万时

    \(f[i][j]\)表示前\(i\)个数分\(k\)段的最小值

    \(f[i][j]=min\{f[i-1][j],0(\)\(f[i-1][j-1]\leq{ans})\}+a_i\)
    判断\(f[n][k]\)是否\(\leq{ans}\)即可.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define K2 15
    #define K1 105
    #define N 50005
    #define M 1000000000
    #define INF 1500000000
    #define min(x,y) x<y?x:y
    using namespace std;
    typedef long long ll;
    int f1[K1][K1],f2[N][K2],a[N],n,k,l,r,mid;
    inline bool cmp1(){
    	for(int i=1;i<=n;++i)
    		if(a[i]<0) return false;
    	return true;
    }
    inline bool chk1(int mx){
    	int cnt=1,sum=a[1];
    	for(int i=2;i<=n;++i)
    		if(sum+a[i]>mx){
    			++cnt;sum=a[i];
    		}
    		else sum+=a[i];
    	return cnt<=k;
    }
    inline bool cmp2(){
    	for(int i=1;i<=n;++i)
    		if(a[i]>0) return false;
    	return true;
    }
    inline bool chk2(int mx){
    	int cnt=1,sum=a[1];
    	for(int i=2;i<=n;++i)
    		if(sum+a[i]>mx){
    			++cnt;sum=a[i];
    		}
    		else sum+=a[i];
    	return cnt<=k;
    }
    inline bool chk3(int mx){
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=k;++j)
    			f1[i][j]=INF+1;
    	f1[1][1]=a[1];
    	for(int i=2;i<=n;++i){
    		for(int j=1;j<=k;++j){
    			f1[i][j]=min(f1[i][j],f1[i-1][j]+a[i]);
    			if(f1[i-1][j-1]<=mx) f1[i][j]=min(f1[i][j],a[i]);
    		}
    	}
    	return f1[n][k]<=mx;
    }
    inline bool chk4(int mx){
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=k;++j)
    			f2[i][j]=INF+1;
    	f2[1][1]=a[1];
    	for(int i=2;i<=n;++i){
    		for(int j=1;j<=k;++j){
    			f2[i][j]=min(f2[i][j],f2[i-1][j]+a[i]);
    			if(f2[i-1][j-1]<=mx) f2[i][j]=min(f2[i][j],a[i]);
    		}
    	}
    	return f2[n][k]<=mx;
    }
    inline void Aireen(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	if(n<=100){
     		l=-INF;r=INF;
    		while(l<r){
    			mid=(int)((ll)(l+r)>>1);
    			if(chk3(mid)) r=mid;
    			else l=mid+1;
    		}
    		printf("%d\n",l);
    		return;
    	} 
    	if(k<=10){
     		l=-INF;r=INF;
    		while(l<r){
    			mid=(int)((ll)(l+r)>>1);
    			if(chk4(mid)) r=mid;
    			else l=mid+1;
    		}
    		printf("%d\n",l);
    		return;
    	} 
    	if(cmp1()){
    		r=INF;
    		while(l<r){
    			mid=l+(r-l>>1);
    			if(chk1(mid)) r=mid;
    			else l=mid+1;
    		}
    		printf("%d\n",l);
    		return;
    	}
    }
    int main(){
    	freopen("divide.in","r",stdin);
    	freopen("divide.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    100分

    \(l\)为最少合法划分段数,\(r\)为最多合法划分段数.
    \(l\leq{ans}\leq{r}\),则可行.
    \(f[i]\)表示前\(i\)个数最少划分段数,\(g[i]\)表示前\(i\)个数最多划分段数.

    \(f[i]=min\{f[j]\}+1\;(s[i]-s[j]\leq{ans})\)

    \(g[i]=max\{g[j]\}+1\;(s[i]-s[j]\leq{ans})\)

    找到\(i\)能延伸到的最左端\(lef\),询问\([lef,i-1]\)的最值,树状数组维护前缀最大值即可.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define K2 15
    #define K1 105
    #define N 50005
    #define M 1000000000
    #define INF 1500000000
    #define min(x,y) x<y?x:y
    using namespace std;
    typedef long long ll;
    int a[N],f[N],s[N],p[N],pre[N],n,m,k,l,r,mid;
    inline int lowbit(int x){
    	return x&(-x);
    }
    inline void add1(int k,int x){
    	for(int i=k;i;i-=lowbit(i)){
    		s[i]=min(s[i],x);
    	}
    }
    inline int ask1(int k){
    	int ret=n;
    	for(int i=k;i<=m;i+=lowbit(i)){
    		ret=min(ret,s[i]);
    	}
    	return ret;
    }
    inline void add2(int k,int x){
    	for(int i=k;i;i-=lowbit(i))
    		s[i]=max(s[i],x);
    }
    inline int ask2(int k){
    	int ret=-n;
    	for(int i=k;i<=m;i+=lowbit(i))
    		ret=max(ret,s[i]);
    	return ret;
    }
    inline bool chk(int mx){
    	int l,r,x;
    	for(int i=1;i<=m;++i)
    		s[i]=n+1;
    	x=lower_bound(p+1,p+1+m,0)-p;
    	add1(x,0);
    	for(int i=1,j;i<=n;++i){
    		j=lower_bound(p+1,p+1+m,pre[i]-mx)-p;
    		f[i]=ask1(j)+1;
    		j=lower_bound(p+1,p+1+m,pre[i])-p;
    		add1(j,f[i]);
    	}
    	l=f[n];
    	for(int i=1;i<=m;++i)
    		s[i]=-n-1;
    	x=lower_bound(p+1,p+1+m,0)-p;
    	add2(x,0);
    	for(int i=1,j;i<=n;++i){
    		j=lower_bound(p+1,p+1+m,pre[i]-mx)-p;
    		f[i]=ask2(j)+1;
    		j=lower_bound(p+1,p+1+m,pre[i])-p;
    		add2(j,f[i]);
    	}
    	r=f[n];
    	return l<=k&&k<=r;
    }
    inline void Aireen(){
    	scanf("%d%d",&n,&k);
    	for(int i=1;i<=n;++i){
    		scanf("%d",&a[i]);
    		p[++m]=pre[i]=pre[i-1]+a[i];
    	}
    	p[++m]=0;
    	sort(p+1,p+1+m);
    	m=unique(p+1,p+1+m)-p-1;
    	l=-INF;r=INF;
    	while(l<r){
    		mid=(int)((ll)(l+r)>>1);
    		if(chk(mid)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d\n",l);
    }
    int main(){
    	freopen("divide.in","r",stdin);
    	freopen("divide.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    jquery attribute!=value选择器 语法
    jquery attribute=value选择器 语法
    jquery attribute选择器 语法
    jquery visible 选择器 语法
    jquery empty选择器 语法
    jquery contains选择器 语法
    jquery animated选择器 语法
    jquery header选择器 语法
    jquery lt选择器 语法
    jquery gt选择器 语法
  • 原文地址:https://www.cnblogs.com/AireenYe/p/CLYZ2017day10.html
Copyright © 2011-2022 走看看