20200820校测

T1:

给出一个n个点的图,编号依次为1~n。编号为i和j的两点之间边权为c*(i^j)(c为给定常数)
另外还有m条边,边权给定。给出A,B,求出A到B的最短路
题目链接

solution:

考虑异或这种操作。其实不用建出n*n条边,只需要如此建边:
比如5(101)号节点,只用向4(100),7(111),1(001)连边即可
即向每一个二进制位取异或连边
而比如5(101)到6(110)的边,可通过5(101)到4(100)到6(110)如此到达
注意下0号节点也需要连边

code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef pair<int,int> pii;
int n,m,c,h,st,ed;
struct edge{int to,w;};
vector<edge>v[N];
inline int dijkstra()
{
	int d[N];fill(d,d+n+1,1e9);
	d[st]=0;
	priority_queue<pii,vector<pii>,greater<pii> >pq;
	pq.push(make_pair(0,st));
	while(!pq.empty())
	{
		int u=pq.top().second;pq.pop();
		for(int i=0;i<v[u].size();++i)
		{
			int j=v[u][i].to,w=v[u][i].w;
			if(d[j]>d[u]+w)
			{
				d[j]=d[u]+w;
				pq.push(make_pair(d[j],j));
			}
		}
	}
	return d[ed];
}
int main()
{
	scanf("%d%d%d",&n,&m,&c);
	for(h=0;(1<<h)<=n;++h);
	for(int i=1;i<=m;++i)
	{
		int f,t,vv;scanf("%d%d%d",&f,&t,&vv);
		v[f].push_back({t,vv});
	}
	for(int i=0;i<=n;++i)
		for(int j=0;j<=h;++j)
		{
			int k=i^(1<<j);
			if(k>n)continue;
			v[i].push_back({k,(1<<j)*c});
		}
	scanf("%d%d",&st,&ed);
	cout<<dijkstra();
	return 0;
}

T2:

在字节山脉有n座山峰,每座山峰有它的高度。有些山峰之间有双向道路相连,共m条路径,每条路径有一个困难值,这个值越大表示越难走,现在有q组询问,每组询问询问从点开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。
\(n<=10^5,m,q<=5*10^5\)
山峰高度\(hi<=10^9\)
题目链接

solution:

首先对整个图用kruskal求出kruskal重构树
而后对于每一个询问,通过在重构树上倍增求出它所对应的区间
问题转化为静态区间第k大,上主席树即可
需要注意:kruskal重构树节点的val要初始化
另外主席树注意空间开够及一些小细节
部分实现可以封装简化

code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5,maxm=6e5+5;
int N,M,Q,T,cnt,H[maxn],B[maxn];
int Fa[maxn];
int Nd,ch[maxn<<1][2],val[maxn<<1],nH[maxn],st[maxn<<1],ed[maxn<<1];
int pr[maxn<<1][20];
int rt[maxn],o,l[maxn*40],r[maxn*40],s[maxn*40];
vector<int>new_H;
struct Smer{int num,id;}_H[maxn];
struct edge{int fr,to,w;}e[maxm];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
inline bool cmp1(Smer x,Smer y){return x.num<y.num;}
inline bool cmp2(Smer x,Smer y){return x.id<y.id;}
inline bool cmp(edge x,edge y){return x.w<y.w;}
int fd(int x){return Fa[x]==x?x:Fa[x]=fd(Fa[x]);}
inline void smer()
{
	Smer cpyH[maxn];cnt=0;
	for(int i=1;i<=N;++i)
		cpyH[i].num=H[i],cpyH[i].id=i;
	sort(cpyH+1,cpyH+N+1,cmp1);
	for(int i=1;i<=N;++i)
	{
		if(i==1||cpyH[i].num!=cpyH[i-1].num)_H[i].num=++cnt;
		else _H[i].num=cnt;
		_H[i].id=cpyH[i].id;
	}
	sort(_H+1,_H+N+1,cmp2);
	for(int i=1;i<=N;++i)B[_H[i].num]=H[i];
}
inline void kruskal()
{
	memset(val,127,sizeof(val));
	fill(val+1,val+N+1,0);
	for(int i=1;i<=N*2;++i)Fa[i]=i;
	sort(e+1,e+M+1,cmp);
	Nd=N;
	int ct=0;
	for(int i=1;i<=M;++i)
	{
		int aa=e[i].fr,bb=e[i].to;
		int faa=fd(aa),fbb=fd(bb);
		if(faa==fbb)continue;
		++ct;
		val[++Nd]=e[i].w;
		ch[Nd][0]=faa;ch[Nd][1]=fbb;
		Fa[faa]=Fa[fbb]=Nd; 
		if(ct==N-1)break;
	}
}
void dfs(int pos,int depth,int Pr)
{
	pr[pos][0]=Pr;
	for(int i=1;(1<<i)<=depth;++i)
		pr[pos][i]=pr[pr[pos][i-1]][i-1];
	if(!val[pos])
	{
		st[pos]=ed[pos]=new_H.size();
		new_H.push_back(pos);
		return;
	}
	dfs(ch[pos][0],depth+1,pos);dfs(ch[pos][1],depth+1,pos);
	st[pos]=st[ch[pos][0]];ed[pos]=ed[ch[pos][1]];
}
inline void get_interval(int pos,int _val,int &ll,int &rr)
{
	for(int i=19;i>=0;--i)
		if(val[pr[pos][i]]<=_val)
			pos=pr[pos][i];
	ll=st[pos];rr=ed[pos];
}
void update(int &_rt,int pre_rt,int p,int ll,int rr)
{
	_rt=++o;
	if(ll==rr){s[_rt]=s[pre_rt]+1;return;}
	int mid=ll+rr>>1;
	l[_rt]=l[pre_rt];r[_rt]=r[pre_rt];
	if(p<=mid)update(l[_rt],l[pre_rt],p,ll,mid);
	else update(r[_rt],r[pre_rt],p,mid+1,rr);
	s[_rt]=s[l[_rt]]+s[r[_rt]];
}
int query(int rt1,int rt2,int ll,int rr,int kk)
{
	if(s[rt2]-s[rt1]<kk)return -1;
	if(ll==rr)return ll;
	int rcnt=s[r[rt2]]-s[r[rt1]],mid=ll+rr>>1;
	if(kk>rcnt)return query(l[rt1],l[rt2],ll,mid,kk-rcnt);
	else return query(r[rt1],r[rt2],mid+1,rr,kk);
}
int main()
{
	N=read();M=read();Q=read();T=read();
	for(int i=1;i<=N;++i)H[i]=read();
	smer();
	for(int i=1;i<=M;++i)
	{
		int aa=read(),bb=read(),cc=read();
		e[i].fr=aa;e[i].to=bb;e[i].w=cc;
	}
	kruskal();
	new_H.push_back(0);
	for(int i=1;i<=N*2-1;++i)
		if(fd(i)==i)dfs(i,1,0);
	for(int i=1;i<new_H.size();++i)nH[i]=_H[new_H[i]].num;
	for(int i=1;i<=N;++i)
		update(rt[i],rt[i-1],nH[i],1,N);
	int lastans=0;
	while(Q--)
	{
		int vv=read(),xx=read(),kk=read(),ll=0,rr=0;
		if(T)vv^=lastans,xx^=lastans,kk^=lastans;
		get_interval(vv,xx,ll,rr);
		int ans=query(rt[ll-1],rt[rr],1,N,kk);
		if(ans==-1)puts("-1"),lastans=0;
		else printf("%d\n",B[ans]),lastans=B[ans];
	}
	return 0;
}

T3:

平面直角坐标系内给出n个点,求一个纵坐标已给出的点使得它到那n个点的距离之和最小
\(n<=10^5\)
题目链接

solution:

设出距离之和函数f(x),而后对其求导得到g(x),易知g(x)单调递增(其实是不会证明)
然后二分求得其零点,带入原函数即可
注意:精度问题以及求导不要求错了

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n;
double k,x[N],y[N],z[N],w[N];
inline bool ck(double p)
{
	double num=0.0;
	for(int i=1;i<=n;++i)
		num+=100*(p*2-z[i])/sqrt(p*p-z[i]*p+w[i]);
	return num>0;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%lf%lf",&x[i],&y[i]);
	scanf("%lf",&k);
	for(int i=1;i<=n;++i)
	{
		z[i]=x[i]*2.0;
		w[i]=(y[i]-k)*(y[i]-k)+x[i]*x[i];
	}
	double l=-1.0e9,r=1.0e9;
	for(int i=1;i<=150;++i)
	{
		double mid=(l+r)/2.0;
		if(ck(mid)) r=mid;
		else l=mid;
	}
	long double ans=0.0;
	for(int i=1;i<=n;++i)
		ans+=sqrt((x[i]-r)*(x[i]-r)+(y[i]-k)*(y[i]-k));
	cout<<fixed<<setprecision(0)<<ans;
	return 0;
}
posted @ 2020-08-21 08:36  BILL666  阅读(148)  评论(0编辑  收藏  举报