【泛刷题】gym101471 world final 2017 (A~F)

【前言】
要开学了,比较颓废,龟速水题。
2200+,2300-居然看不了gym的数据qwq
由于比较长我觉得完全可以分两篇来写。
有不少代码是看着写的

这里是上篇。
下篇请移步这里

【题目】
gym

A.Airport Construction

给定一个 n n n个点的多边形,求多边形内最长线段的长度。
n ≤ 200 , ∣ x i ∣ , ∣ y i ∣ ≤ 1 0 9 n\leq 200,|x_i|,|y_i|\leq 10^9 n200,xi,yi109

可以发现答案的直线一定经过两个端点。
于是枚举答案经过哪两个端点,然后和其他线段求交,往左右两边扩展即可。
复杂度 O ( n 3 ) O(n^3) O(n3)

#include<bits/stdc++.h>
#define fi first
#define se second
#define mkp make_pair
using namespace std;

typedef double db;
const int N=205;
const db eps=1e-8;
int n,cnt;
db ans;
pair<db,int> t[N];


struct Point
{
	db x,y;
	Point(db _x=0,db _y=0):x(_x),y(_y){}
	Point operator + (const Point&rhs)const{return Point(x+rhs.x,y+rhs.y);}
	Point operator - (const Point&rhs)const{return Point(x-rhs.x,y-rhs.y);}
	db operator * (const Point&rhs)const{return x*rhs.y-y*rhs.x;}
	db getlen(){return sqrt(x*x+y*y);}
}a[N];

struct Line
{
	Point a,b;
	Line (Point _a=Point(0,0),Point _b=Point(0,0)):a(_a),b(_b){}
};

db getdis(const Line x,const Line y)
{
	return ((y.a-x.a)*(y.b-x.a))/((y.b-y.a)*(x.b-x.a))*(x.b-x.a).getlen();
}

int O(db x){return fabs(x)<eps?0:(x<0?-1:1);}

void solve(Line l)
{
	cnt=0;
	for(int i=1;i<=n;++i)
	{
		Line r=Line(a[i],a[i+1]);
		int p=O((l.b-l.a)*(r.a-l.a)),q=O((l.b-l.a)*(r.b-l.a));
		if(p==q) continue;//no cross
		t[++cnt]=mkp(getdis(l,r),(p>q?1:-1)*((p && q)?2:1));
	}
	sort(t+1,t+cnt+1);
	int res=0;db len=0;
	for(int i=1;i<=cnt;++i)
	{
		if(res) len+=t[i].fi-t[i-1].fi;
		else ans=max(ans,len),len=0;
		res+=t[i].se;
	}
	ans=max(ans,len);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
	a[n+1]=a[1];
	for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) solve(Line(a[i],a[j]));
	printf("%.10lf\n",ans);
	return 0;
}

B.Get a Clue!

3 3 3种类型的卡牌,每种类型分别有 6 , 6 , 9 6,6,9 6,6,9张,三种类型的卡牌分别会被移除一张。

现在有四个人游戏,每个人按顺序拿到剩下的卡牌(可能数量不同),有 n n n轮轮流的决策,每轮询问人提出一个题意猜测被移除的牌分别是什么,然后按顺时针方向反驳。若一个人手中有可以反驳的牌,需要展示任意一张可反驳牌给询问人,然后结束这轮决策。

假设你是 1 1 1号玩家,现在给出每轮的决策(有些结果不可见,即询问人和反驳人都不是你),问你可以是否可以确定每种类型的卡牌移除了什么。
n ≤ 50 n\leq 50 n50

数据范围看起来很小,于是我们枚举一下移除了什么,然后再枚举一下其他三个人的牌,判断一遍。这样子显然是过不了的。注意到每个状态对与一个玩家是否合法是独立的,因此不妨先搜出每个人合法的状态有哪些,再进行上面的爆搜。

复杂度玄学。

#include<bits/stdc++.h>
#define ppc(x) __builtin_popcount(x)
using namespace std;

const int N=51,P=4,D=1<<21,num[P]={5,5,4,4};
int n,ban;
int sz[P],lim[P][N],hv[P+1];
char ch[P];
vector<int>ans;

bool dfs(int dp,int msk,int r)
{
	if(dp==P) return 1;
	if(ppc(msk)==num[dp]) 
	{
		for(int i=0;i<sz[dp];++i) 
			if(((msk&lim[dp][i])>0)^((lim[dp][i]&D)>0)) return 0;
		ban^=msk^hv[dp];
		bool res=dfs(dp+1,hv[dp+1],~ban);
		ban^=msk^hv[dp];
		return res;
	}
	for(;;)
	{
		int t=r&(-r);
		if(t>=D) break;
		if(dfs(dp,msk|t,r^=t)) return 1;
	}
	return 0;
}

int gnex(int x){return (x+1)%P;}
void init()
{
	scanf("%d",&n);
	for(int i=0;i<5;++i) scanf("%s",ch),hv[0]|=1<<(ch[0]-'A');
	for(int i=0;i<n;++i)
	{
		int msk=0;
		for(int j=0;j<3;++j) scanf("%s",ch),msk|=1<<(ch[0]-'A');
		for(int j=1,k=gnex(i);j<P;++j,k=gnex(k)) 
		{
			scanf("%s",ch);
			if(ch[0]=='-') lim[k][sz[k]++]=msk;
			else
			{
				if(ch[0]=='*') lim[k][sz[k]++]=msk|D;
				else hv[k]|=1<<(ch[0]-'A');
				break;
			}
		} 
	}
	for(int i=0;i<P;++i)
	{
		ban|=hv[i];
		sort(lim[i],lim[i]+sz[i]);
		sz[i]=unique(lim[i],lim[i]+sz[i])-lim[i];
	}
	for(int i=0;i<6;++i) if(~ban&(1<<i))
		for(int j=6;j<12;++j) if(~ban&(1<<j))
			for(int k=12;k<21;++k) if(~ban&(1<<k))
			{
				//printf("%d %d %d\n",i,j,k);
				int msk=(1<<i)+(1<<j)+(1<<k);
				ban^=msk;
				if(dfs(1,hv[1],~ban)) ans.push_back(msk);
				ban^=msk;
			}
	//for(int i=0;i<(int)ans.size();++i) printf("%d\n",ans[i]);
}

void getans()
{
	int i,j,tot=(int)ans.size();
	for(i=0;i<6;++i) 
	{
		for(j=0;j<tot;++j) if(~ans[j]&(1<<i)) break;
		if(j==tot) {ch[0]='A'+i;break;}
	}
	if(i==6) ch[0]='?';
	for(i=6;i<12;++i)
	{
		for(j=0;j<tot;++j) if(~ans[j]&(1<<i)) break;
		if(j==tot) {ch[1]='A'+i;break;}
	}
	if(i==12) ch[1]='?';
	for(i=12;i<21;++i)
	{
		for(j=0;j<tot;++j) if(~ans[j]&(1<<i)) break;
		if(j==tot) {ch[2]='A'+i;break;}
	}
	if(i==21) ch[2]='?';
	ch[3]='\0';puts(ch);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	init();
	getans();
	return 0;
}

C.Mission Improbable

一个空间中有一堆箱子排成 n n n m m m列,每个位置的箱子个数分别为 x i , j x_{i,j} xi,j
问最多能去掉多少个箱子,使得重新放置这些箱子后,三视图不变。
n , m ≤ 100 , x i , j ≤ 1 0 9 n,m\leq 100,x_{i,j}\leq 10^9 n,m100,xi,j109

观察到对于俯视图,只需要这个位置上不拿完即可。对于正视图和侧视图,要求这一行/列的最大值不变。

如果有一行一列的最大值相同,我们不妨将这两个最大值合并起来,即放在它们的交点位置。

于是我们将最大值相同的行列之间连边,这样子实际上就是一个二分图最大匹配问题,尽可能将最大值放在交点位置。然后最后统计一遍答案即可。

一个点数 O ( n ) O(n) O(n),边数 O ( n 2 ) O(n^2) O(n2)的二分图匹配,就不写网络流了吧- -。

#include<bits/stdc++.h>
using namespace std;

const int N=205;
int n,m,tot;
int head[N],mr[N],ml[N],a[N][N],vis[N],used[N];
long long ans;

struct Tway{int v,nex;}e[N*N];
void add(int u,int v){e[++tot]=(Tway){v,head[u]};head[u]=tot;}

bool dfs(int x)
{
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(vis[v]) continue;
		vis[v]=1;if(!used[v] || dfs(used[v])) {used[v]=x;return 1;}
	}
	return 0;
}
void solve()
{
	for(int i=1;i<=n;++i) if(mr[i]) ans-=mr[i]-1;
	for(int i=1;i<=m;++i) if(ml[i]) ans-=ml[i]-1;
	for(int i=1;i<=n;++i)
	{
		memset(vis,0,sizeof(vis));
		if(mr[i] && dfs(i)) ans+=mr[i]-1;
	}
	printf("%lld\n",ans);
}
void init()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
	{
		scanf("%d",&a[i][j]);
		mr[i]=max(mr[i],a[i][j]);ml[j]=max(ml[j],a[i][j]);
		if(a[i][j]) ans+=a[i][j]-1;
	}	
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j)
		if(mr[i]==ml[j] && mr[i] && a[i][j]) add(i,n+j);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	init();solve();
	return 0;
}

D.Money for Nothing

n n n个生产公司和 m m m个消费公司。每个生产公司有开始生产时间和零件价格 ( s i , a i ) (s_i,a_i) (si,ai),你可以在生产时间开始每天在生产公司买一个零件。每个消费公司有截止消费时间和接收零件价格 ( t i , b i ) (t_i,b_i) (ti,bi),你可以在截至消费时间之前每天卖给消费公司零件。问在选择一家生产公司和一家消费公司的情况下最大利润。
n , m ≤ 5 × 1 0 5 n,m\leq 5\times 10^5 n,m5×105,其他数字不超过 1 0 9 10^9 109

实际上要求的就是 max ⁡ { ( t i − s i ) ∗ ( b i − a i ) } \max\{ (t_i-s_i)*(b_i-a_i)\} max{(tisi)(biai)}

这个东西显然是有单调性的,即对于 t i t_i ti越大的消费公司选择 s i s_i si一定也是单调增的。

考虑问题的本质实际上就是求矩形面积最大emmm,证明就随便取四个点比较一下大小就行了。

分治可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int N=5e5+10,inf=0x3f3f3f3f;
const ll llinf=1ll*inf*inf;
int n,m,cnt;
pii a[N],b[N];
ll ans,f[N];

void solve(int l,int r,int L,int R)
{
	if(l>r) return;
	int mid=(l+r)>>1,pos;
//cerr<<l<<" "<<r<<" "<<L<<" "<<R<<endl;
	for(int i=L;i<=R;++i) 
	{
		if(b[i].fi<a[mid].fi && b[i].se<a[mid].se) continue;
		ll t=1ll*(b[i].fi-a[mid].fi)*(b[i].se-a[mid].se);
		if(f[mid]<t) f[mid]=t,pos=i;
	}
	ans=max(ans,f[mid]);
	solve(l,mid-1,L,pos);solve(mid+1,r,pos,R);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d%d",&a[i].fi,&a[i].se);
	for(int i=1;i<=m;++i) scanf("%d%d",&b[i].fi,&b[i].se);
	sort(a+1,a+n+1);
	for(int i=1,now=inf;i<=n;)
	{
		if(a[i].se<now) f[cnt]=-llinf,a[++cnt]=a[i],now=a[i].se;
		for(int j=a[i].fi;i<=n && a[i].fi==j;++i);
	}
	n=cnt;cnt=0;
	sort(b+1,b+m+1,greater<pii>());
	for(int i=1,now=0;i<=m;)
	{
		if(b[i].se>now) b[++cnt]=b[i],now=b[i].se;
		for(int j=b[i].fi;i<=m && b[i].fi==j;++i);
	}
	m=cnt;reverse(b+1,b+m+1);
	solve(1,n,1,m);
	printf("%lld\n",ans);
	return 0;
}

E.Need for Speed

n n n段行程,共花费了 t t t的时间。已知每段行程的距离 d i d_i di和表盘读数是 s i + c s_i+c si+c,求常数 c c c
∣ d i ∣ , ∣ s i ∣ , n ≤ 1000 , t ≤ 1 0 6 |d_i|,|s_i|,n\leq 1000,t\leq 10^6 di,si,n1000,t106

显然可以二分答案,答案的上界应该是 2 × 1 0 6 2\times 10^6 2×106,下界是 − 1 0 3 -10^3 103。复杂度 O ( n log ⁡ a n s ) O(n\log ans) O(nlogans)
(按道理来说上界是 1 0 6 10^6 106吧,然而过不了。

#include<bits/stdc++.h>
using namespace std;

typedef double db;
const int N=1005;
const db eps=1e-10;
int n;
db T,d[N],s[N];

bool check(db c)
{
	db tot=0;
	for(int i=1;i<=n;++i) 
	{
		if(s[i]+c<eps) return 1;
		tot+=d[i]/(s[i]+c);
	}
	//cerr<<c<<" "<<tot<<endl;
	return tot>T;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%lf",&n,&T);
	for(int i=1;i<=n;++i) scanf("%lf%lf",&d[i],&s[i]);
	db l=-1000,r=2e6;
	while(l+eps<r)
	{
		db mid=(l+r)/2;
		if(check(mid)) l=mid+eps;
		else r=mid-eps;
	}
	printf("%.10lf\n",l-eps);
	return 0;
}

F.Posterize

给定 n n n种匹配点,强度为 r i r_i ri,有 p i p_i pi个。从中选择 k k k种强度,定义平方误差为 ∑ i = 1 n r i ∗ min ⁡ 1 ≤ j ≤ k ( r i − r j ) 2 \sum_{i=1^n} r_i*\min\limits_{1\leq j\leq k} (r_i-r_j)^2 i=1nri1jkmin(rirj)2。最小化平方误差。
所有数字不超过 256 256 256

f i , j f_{i,j} fi,j表示前 i i i种强度选择了 j j j种,且第 i i i种一定选择时的最小平方误差和。那么转移时枚举上一种选了什么,不难得到第 j − 1 j-1 j1种和第 j j j种的贡献区间,记录前缀和优化转移即可。

实现时可以直接按 n = 256 n=256 n=256来做,还可以使用区间 DP \text{DP} DP的方式来计算。

复杂度 O ( n 3 ) O(n^3) O(n3)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=256,M=N+10;
int n,K;
ll ans,sum,cnt[M],sq[M],f[M][M];//wrong because use N

ll calc(int l,int r,int p){return (cnt[r]-cnt[l])*p*p-(sq[r]-sq[l])*p;}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	scanf("%d%d",&n,&K);
	for(int i=1,x;i<=n;++i) scanf("%d",&x),scanf("%lld",&cnt[x]),sq[x]=cnt[x]*x*2,sum+=cnt[x]*x*x;
	for(int i=1;i<N;++i) cnt[i]+=cnt[i-1],sq[i]+=sq[i-1];
	memset(f,0x3f,sizeof(f));ans=f[0][0];
	for(int i=1;i<=K;++i) 
	{
		if(i==1) for(int j=0;j<N;++j) f[1][j]=cnt[j]*j*j-sq[j]*j;
		else
		{
			for(int j=i-1;j<N;++j) for(int k=i-2;k<j;++k)
			{
				int mid=(j+k)>>1;
				f[i][j]=min(f[i][j],f[i-1][k]+calc(k,mid,k)+calc(mid,j,j));
			}
		}
		if(i==K) for(int j=i-1;j<N;++j) f[i][j]+=calc(j,N-1,j),ans=min(ans,f[i][j]);
	}
	printf("%lld\n",ans+sum);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值