2018_9_22 模拟赛

111 篇文章 0 订阅
47 篇文章 0 订阅

前言:

渐入佳境233


JZOJ 5771 遨游

题目

一个双向图,找出一对数 [ L , R ] , L ≤ R [L,R],L\leq R [L,R],LR,使双向图从起点到终点的边权在该区间范围内,且使 L L L尽量大, R R R尽量小


分析

可以发现这是一道有关最大和最小的题目,then就想到了二分答案,二分下界,用最小生成树kruskal判断最长边的长度,若能形成最小生成树,那么记录真正的上界和下界,kruskal使用并查集


代码

#include <cstdio>
#include <deque>
#include <algorithm>
#define rr register
#define N 50000
using namespace std;
struct node{int x,y; double w; int next;}e[N<<1|1];
int n,m,s,t,ls[N|1],tot,g[5001],f[N|1],lim,rim;
inline int in(){
	rr int ans=0; rr char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
inline int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}//路径压缩
inline void pd(int x){
	double dow=2e9;
	for (rr int i=1;i<=tot;++i) f[i]=i;
	for (rr int i=1;i<=m;++i){
		if (e[i].w<x) continue;
		int fa=getf(e[i].x),fb=getf(e[i].y);
		dow=dow<e[i].w?dow:e[i].w;//求下界
		if (fa!=fb){
			int fc=fa<fb?fa:fb;
			f[fc]=fa+fb-fc;//并查集按秩合并
			if (getf(s)==getf(t)){
				lim=dow; rim=e[i].w+0.9; //求上下界
				return;
			}
		}
	}
	return;
}
inline bool cmp(node a,node b){return a.w<b.w;}
signed main(){
	n=in(); m=in(); rr int l=0,r=0;
	for (rr int i=1;i<=m;++i){
		rr int x=in(); ls[x]=i;
		e[i]=(node){x,in(),in(),ls[x]};//建图
	}
	for (rr int i=1;i<=n;++i){
		rr int t=in(); tot+=t;
		while (t--) f[in()]=i;//记录省份
	}
	for (rr int i=1;i<=n;++i) g[i]=in();
	for (rr int i=1;i<=m;++i){
		if (f[e[i].x]==f[e[i].y]) e[i].w*=g[f[e[i].x]]/100.0;//优惠价
		else e[i].w*=(g[f[e[i].x]]+g[f[e[i].y]])/200.0;
		r=r>e[i].w+1?r:e[i].w+1;//上界剪枝
	}
	s=in(); t=in(); sort(e+1,e+1+m,cmp);//kruskal需要快排
	while (l<r){
		rr int mid=(l+r)>>1; pd(mid);//判断是否形成最小生成树
		if (f[s]==f[t]) l=mid+1; else r=mid;
	}
	return !printf("%d %d",lim,rim);//输出上下界
}

JZOJ 5772 今天你AK了吗?

题目

求1到 n n n全排列字典序第k小


分析

k = x 1 ( n − 1 ) ! + x 2 ( n − 2 ) ! + ⋯ + x n − 1 k=x_1(n-1)!+x_2(n-2)!+\cdots+x_{n-1} k=x1(n1)!+x2(n2)!++xn1
所以可以想到逆康拓展开,因为求的是第k小,所以先要减掉1,然后答案根据证明也就是k除以1到 n ! n! n!的商,之后二分 x 1 , x 2 , … , x n − 1 , x n x_1,x_2,\dots,x_{n-1},x_n x1,x2,,xn1,xn的答案,用树状数组维护是否用过即可


代码

#include <cstdio>
#include <cstring>
#define rr register
#define mod (ll)(1e15)
typedef long long ll;
ll n,a[1335]; int b[100001],c[100001];
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int main(){
	rr char u=getchar(); a[0]=1; rr int len=0;
    while (u<48||u>57) u=getchar(); rr ll k=1;
    while (u>47&&u<58) n=(n<<3)+(n<<1)+u-48,u=getchar();
    while (u<48||u>57) u=getchar();
    while (u>47&&u<58) b[++len]=u-48,u=getchar();
    for (rr int i=len,j=0;i>=1;i--){//压位
		j++; a[a[0]]+=b[i]*k; k=(k<<1)+(k<<3);
		if (j==15) j=0,k=1,a[++a[0]]=0; 
	}a[len=1]--;
	while (a[len]<0) a[++len]--,a[len-1]+=mod;
	while (a[0]>1&&!a[a[0]]) a[0]--; b[n]=0;
	for (rr int i=2;i<=n;i++){//高精除单精求余数
		rr ll g=0;
		for (rr int j=a[0];j;j--){
			a[j]+=g*mod;
			g=a[j]%i; a[j]/=i; 
		}
		b[n-i+1]=g;
		while (a[0]>1&&!a[a[0]]) a[0]--;
	}
	for (rr int i=1;i<=n;i++) c[i]=i&-i;//优化,初始化树状数组O(n)
	for (rr int i=1;i<=n;i++){
		rr int l=1,r=n;
		while (l<r){//二分查找
			rr int mid=l+r>>1;
			rr int sum=0,x=mid;
			while (x) sum+=c[x],x-=x&-x;//求1到mid的个数        
			if (sum<=b[i]) l=mid+1; else r=mid;
		}
		print(l); putchar(' ');
		while (l<=n) c[l]--,l+=l&-l;//用掉了这个数在树状数组减掉
	}
    return 0;
}

JZOJ 5773 简单数学题

题目

给定 n n n,求 n − 1 2 T n − T \frac{n-\frac{1}{2}T}{n-T} nTn21T T T T的正整数解


分析

作为本次比赛最简单的一道数论题,大概讲讲证明就行了
x = n − T x=n-T x=nT,那么原式= n − 1 2 ( n − x ) x = n 2 x + 1 2 \frac{n-\frac{1}{2}(n-x)}{x}=\frac{n}{2x}+\frac{1}{2} xn21(nx)=2xn+21
由于原式的结果是正整数, 2 ∗ 原 式 = n x + 1 2*原式=\frac{n}{x}+1 2=xn+1
所以需要保证 n m o d &ThinSpace;&ThinSpace; x = 0 且 n m o d &ThinSpace;&ThinSpace; x n \mod x=0且n \mod x nmodx=0nmodx是奇数
那么就可以在 O ( n ) O(\sqrt n) O(n )的时间解决


代码

#include <cstdio>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
ll n; queue<ll>q1; stack<ll>q2;
void print(ll ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int main(){
	scanf("%lld",&n);
	for (register ll i=1;i<n&&i*i<=n;++i)
	if (n%i==0){
		if ((n/i)&1) q2.push(n-i);//当然要用n减回去
		if (i>1&&i&1) q1.push(n-n/i);
	}
	print(q1.size()+q2.size());
	while (q1.size()) putchar(' '),print(q1.front()),q1.pop();
	while (q2.size()) putchar(' '),print(q2.top()),q2.pop();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值