2018.10.11模拟赛

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/83025001

T1

一道类似贪心的模拟题,就是要删除一些让 m i n { a } m i n { b } min\{a\}*min\{b\} 最大

可以先存两个结构体,一个按 a a 排序,一个按 b b 排序,然后枚举 a a 中删掉几个,看相应的 b b 中最多能删掉几个

首先 a a 里删掉 0 0 个的时候, b b 中一定可以删掉 m m 个, a a 不断往后删除,如果它删的那个矩形在 b b 中的位置在前 m m 个,那它就不用再在 b b 中删除了,就这样用两个指针维护一下就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
int t,n,m,pos[maxn],vis[maxn],cas;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f; 
}

struct qwq{
	int a,b,id;
	bool operator <(const qwq &x) const{
		return a<x.a||(a==x.a&&b<x.b);
	}
}tra[maxn],trb[maxn];
inline bool cmp(qwq x,qwq y){return x.b<y.b||(x.b==y.b&&x.a<y.a);}

int main(){
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
	t=rd();
	while(t--){
		n=rd(); m=rd(); cas++;
		for(int i=1;i<=n;i++)
			tra[i].a=rd(),tra[i].b=rd(),tra[i].id=i,
			trb[i].a=tra[i].a,trb[i].b=tra[i].b,trb[i].id=i;
		sort(tra+1,tra+n+1); sort(trb+1,trb+n+1,cmp);
		for(int i=1;i<=n;i++)
			pos[trb[i].id]=i;
		int l=0,r=m+1,mna=0,mnb=0;
		LL ans=1LL*tra[1].a*trb[r].b;
		for(int i=1;i<=m;i++){
			if(pos[tra[i].id]<r) vis[pos[tra[i].id]]=cas;
			else{
				r--;
				while(vis[r]==cas) r--;
			}
			ans=max(ans,1LL*tra[i+1].a*trb[r].b);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

T2

这个题其实有很多种方法可以过掉, s t d std 给的好像是状压 d p + S P F A dp+SPFA ,然后有人还用状压 + d i j k s t r a +dijkstra 过掉

但是!我用的是优秀的 d f s dfs !这真是暴力拍标算的实例啊

可以发现如果点度都是偶数那么一定可以一遍走到,因此只要把所有度数为奇的点拿出来两两匹配,边的长度就是他们的最短路,用之前所有的边的长度加上最小的两两配对的边长度就是答案

然后可以用各种各样的姿势和小剪枝来优化

实测比 s t d std

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 25
#define M 1005
#define inf 0x3f3f3f3f
using namespace std;
int n,m,ans,f[N][N],d[N];
int nw[N],tot,val[N][N],res=inf;
bool vis[N];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline void dfs(int now,int sum,int t){
	if(t==tot/2){
		res=min(res,sum);
		return;
	}
	if(res<=sum) return;
	if(vis[now]) {
		dfs(now+1,sum,t); return;
	}
	vis[now]=1;
	for(int i=now+1;i<=tot;i++)
		if(!vis[i]){
			vis[i]=1;
			dfs(now+1,sum+val[now][i],t+1);
			vis[i]=0;
		}
	vis[now]=0;
	return;
}

int main(){
	freopen("jogging.in","r",stdin);
	freopen("jogging.out","w",stdout);
	n=rd(); m=rd(); memset(f,0x3f,sizeof f);
	for(int i=1;i<=m;i++){
		int x=rd(),y=rd(),z=rd(); d[x]++,d[y]++;
		ans+=z; f[x][y]=f[y][x]=min(f[x][y],z);
	}
	for(int i=1;i<=n;i++) f[i][i]=0;
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	for(int i=1;i<=n;i++)
		if(d[i]&1)
			nw[++tot]=i;
	for(int i=1;i<=tot;i++)
		for(int j=i+1;j<=tot;j++)
			val[i][j]=val[j][i]=f[nw[i]][nw[j]];
	dfs(1,0,0);
	printf("%d\n",ans+res);
	return 0;
}
/*
4 6
1 2 3
2 3 4
3 4 5
1 4 10
1 3 12
2 4 8
*/ 

T3

毒瘤题···

题解说的很清晰了
在这里插入图片描述
具体实现看代码注释吧,还是挺难写的,综合了 t r i e trie 树, m e e t   i n t   t h e   m i d d l e meet\ int\ the\ middle ,二分答案···

总之是一道考验代码能力的好题咯

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define prit pair<LL,LL>
#define mp make_pair
#define fi first
#define se second
#define N 500005
#define K 35
#define M N*31
#define LL long long
using namespace std;
int n,k,a[N],trie[M][2],tot=1,siz[M];
LL val0[K],val1[K],p;
vector< prit > vec1,vec2;

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}

inline void insert(int x){
	int now=1;
	for(int i=k-1;i>=0;i--){
		++siz[now]; 
		if((x>>i)&1)//这一位是1 
			val1[i]+=siz[trie[now][0]];
		else val0[i]+=siz[trie[now][1]];
		if(!trie[now][(x>>i)&1]) now=trie[now][(x>>i)&1]=++tot;
		else now=trie[now][(x>>i)&1];
	}
	++siz[now];
}

inline bool check(prit lim){
	LL res=0;//双指针 
	for(int i=0,j=vec2.size()-1;i<vec1.size();i++){
		while(j>=0 && mp(vec1[i].fi+vec2[j].fi,vec1[i].se|vec2[j].se)>lim)
			j--;//将前一半和后一半合并 
		res+=j+1;
	}
	return res>=p;
}

signed main(){
	freopen("f.in","r",stdin);
	freopen("f.out","w",stdout);
	n=rd(); k=rd(); scanf("%lld",&p);
	for(int i=1;i<=n;i++){
		a[i]=rd(); insert(a[i]);//插入01trie 
	}
	int k1=k/2,k2=k-k1;//meet in the middle 
	for(int i=0;i<(1<<k1);i++){
		LL tmp=0;
		for(int j=0;j<k1;j++)
			if((i>>j)&1) tmp+=val1[j];
			else tmp+=val0[j];
		vec1.push_back(mp(tmp,i));
	}
	for(int i=0;i<(1<<k2);i++){
		LL tmp=0;
		for(int j=0;j<k2;j++)
			if((i>>j)&1) tmp+=val1[j+k1];
			else tmp+=val0[j+k1];
		vec2.push_back(mp(tmp,i<<k1));
	}
	sort(vec1.begin(),vec1.end()); sort(vec2.begin(),vec2.end());//排序 
	LL l=0,r=1LL*(n-1)*n/2,ans=0;//开始二分 
	while(l<=r){
		LL mid=(l+r)>>1;//先二分f值 
		if(check(mp(mid,1<<k))) r=mid-1,ans=mid;
		else l=mid+1;
	}
	LL f=ans;
	l=0,r=(1<<k)-1,ans=0;
	while(l<=r){//再二分res值 
		LL mid=(l+r)>>1;
		if(check(mp(f,mid))) r=mid-1,ans=mid;
		else l=mid+1;
	}
	printf("%lld %lld\n",f,ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/83025001