[牛客国庆集训派对Day1] C[数学] L[最短路] J[线段树]

30 篇文章 0 订阅
27 篇文章 0 订阅

比赛链接
C
链接:https://www.nowcoder.com/acm/contest/201/C
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 1048576K,其他语言2097152K
64bit IO Format: %lld
题目描述
算术是为数不多的会让Kuon感到棘手的事情。通常她会找Haku帮忙,但是Haku已经被她派去买东西了。于是她向你寻求帮助。
给出一个关于变量x,y的不定方程ax+by=c,显然这个方程可能有多个整数解。Kuon想知道如果有解,使得p2x2+p1x+q2y2+q1y最小的一组整数解是什么。为了方便,你只需要输出p2x2+p1x+q2y2+q1y的最小值。
输入描述:
第一行三个空格隔开的整数a,b,c(0 ≤ a,b,c≤ 105)。
第二行两个空格隔开的整数p1,p2(1 ≤ p1,p2 ≤ 105)。
第三行两个空格隔开的整数q1,q2(1 ≤ q1,q2 ≤ 105)。
输出描述:
如果方程无整数解,输出“Kuon”。
如果有整数解,输出p2x2+p1x+q2y2+q1y的最小值。
示例1
输入

2 2 1
1 1
1 1
输出

Kuon
示例2
输入

1 2 3
1 1
1 1
输出

4
分析: 首先当不定方程有解的同时,后面的方程一定有整数解,故我们强行将 y = ( c − a ∗ x ) / b y = (c - a * x) / b y=(cax)/b 带入,然后化简就可以得到一个关于 x x x的二元一次方程. (注意x的取值不是连续的,我们用扩展欧几里得求出一个特解 x 0 x_0 x0,那么 x = x 0 + b / g c d ( a , b ) ∗ t x = x_0 + b / gcd(a, b) * t x=x0+b/gcd(a,b)t) , 而且根据式子我们可以知道a是大于0的,故一定是开口向上的一个二次曲线,最小值 在 x = − b 2 ∗ a x=\frac {-b} {2*a} x=2ab处取, 所以我们只要找到最接近这个位置的x的值就行了. 暴力枚举就行
Code

 
#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n";


typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 1e5 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/

void exgcd(ll a,ll b, ll &d, ll &x, ll &y){
	if(!b) { d=a ; x= 1 ,y = 0;}
	else {
		exgcd(b,a%b,d,y,x);
		y-=a/b*x;
	}
}
int main(){
	ll a, b, c, p1, p2, q1, q2; cin >> a >> b >> c >> p1 >> p2 >> q1 >> q2;
	ll x0, y0, d;
	double c1 = 1.0;
	exgcd(a, b, d, x0, y0);
	if(c % d ) cout << "Kuon" <<"\n";
	else {
//		cout << d <<"\n"
		x0 = x0 * (c / d);
		double aa = p2 * c1 + q2 * a * a * c1 / (b * b);
		double bb = p1 * c1 - a * q1 * c1 / b - q2 * 2 * a * c * c1/ (b * b);
	    double cc =	q1 * c1 * c * c1 / b + q2 * c * c * c1/ (b * b);
//		cout << aa << " " << bb << " " << cc << "\n";
		ll mid = -bb  / ( 2 * aa);
		//cout << b / d <<"\n";
		ll ans = INFF;
		ll bd = b / d;
		for(ll i = mid - 1000000; i <= mid + 1000000; i++){
			double  t = (i * 1.0 - x0) / bd;
			//cout << i <<"\n";	
			if(((ll)t - t) == 0) {
				ans = min(ans, (ll)(i * i * aa + bb * i + cc));
//				cout << "ok " << i << "\n";
			}
		}
		cout << ans <<"\n";
	}
	return 0;
}

L
链接:https://www.nowcoder.com/acm/contest/201/L
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 1048576K,其他语言2097152K
Special Judge, 64bit IO Format: %lld
题目描述
Eagle Jump公司正在开发一款新的游戏。Hifumi Takimoto作为其中的员工,获得了提前试玩的机会。现在她正在试图通过一个迷宫。
这个迷宫有一些特点。为了方便描述,我们对这个迷宫建立平面直角坐标系。迷宫中有两条平行直线 L1:Ax+By+C1=0, L2:Ax+By+C2=0,还有 n 个圆 。角色在直线上、圆上、园内行走不消耗体力。在其他位置上由S点走到T点消耗的体力为S和T的欧几里得距离。
Hifumi Takimoto想从 L1 出发,走到 L2 。请计算最少需要多少体力。
输入描述:
第一行五个正整数 n,A,B,C1,C2 (1≤ n ≤ 1000, -10000 ≤ A,B,C1,C2 ≤ 10000),其中 A,B 不同时为 0。
接下来 n 行每行三个整数 x,y,r(-10000 ≤ x,y ≤ 10000, 1≤ r ≤ 10000) 表示一个圆心为 (x,y),半径为 r 的圆。
输出描述:
仅一行一个实数表示答案。与正确结果的绝对误差或者相对误差不超过 10-4 即算正确。
示例1
输入

2 0 1 0 -4
0 1 1
1 3 1
输出

0.236068
分析: 直接建图跑最短路就行了
代码

 
#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n";

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 1e5 + 11;
const int M = (int) 1e6 + 1000;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/
struct Edge{
	int from, to, nex; double val;
	Edge(){}
	Edge(int _from, int _to, double _val, int _nex){
		from = _from; to = _to; val = _val; nex = _nex;
	}
}edge[M];
int head[N], top;
void init(int n){
	memset(head, -1, sizeof(int)  * (n + 10));
	top = 0;
}
void addedge(int a, int b, double c){
	edge[top] = Edge(a, b, c, head[a]);
	head[a] = top++;
	edge[top] = Edge(b, a, c, head[b]);
	head[b] = top++;
}

bool vis[N];double dis[N];
void spfa(int s, int t){
	queue<int>que;
	que.push(s);	
	for(int i = 0; i <= t; i++) dis[i] = INF * 1.0;
	vis[s]  = 1; dis[s] = 0;
	while(!que.empty()){
		int now = que.front(); que.pop();
		vis[now] =false;
		for(int i = head[now];i != -1; i = edge[i].nex){
			Edge e = edge[i];
			if(dis[now] + e.val < dis[e.to]) {
				dis[e.to] = dis[now] + e.val;
				if(!vis[e.to]) {
					que.push(e.to);
					vis[e.to] = 1;
				}
			}
		}
	}
	printf("%.5lf\n", dis[t]);
}
double xx[N], yy[N], rr[N];
int main(){
	double n, A, B, C1, C2; cin >> n >> A >> B >> C1 >> C2;
	int S = 0, T = n + 1;
	init(T);
	rep(i, 1, n + 1){
		static double x, y, r; cin >> x >> y >> r;
		xx[i] = x; yy[i] = y; rr[i] = r;
		double dis = fabs((A * x + B * y + C1) / sqrt(A * A + B * B)) - r;
		dis = max((double)0.0, dis);
		//      dbg(dis);
		addedge(S, i, dis);
		dis = fabs((A * x + B * y + C2) / sqrt(A * A + B * B)) - r;
		dis = max((double)0.0, dis);
		//      dbg(dis);
		addedge(i, T, dis);
	}
	rep(i, 1, n + 1){
		rep(j, i + 1, n + 1){
			double dis = sqrt((xx[i] - xx[j]) * (xx[i] - xx[j]) + (yy[i] - yy[j]) * (yy[i] - yy[j])) - rr[i] - rr[j];
			dis = max((double)0.0, dis);
			//          dbg(dis);
			addedge(i, j, dis);
		}
	}
	spfa(S, T);
	return 0;
}

J
链接:https://www.nowcoder.com/acm/contest/201/J
来源:牛客网

时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 1048576K,其他语言2097152K
64bit IO Format: %lld
题目描述
阿尔比恩王国(the Albion Kingdom)潜伏着一群代号“白鸽队(Team White Pigeon)”的间谍。在没有任务的时候,她们会进行各种各样的训练,比如快速判断一个文档有没有语法错误,这有助于她们鉴别写文档的人受教育程度。
这次用于训练的是一个含有n个括号的文档。括号一共有m种,每种括号都有左括号和右括号两种形式。我们定义用如下的方式定义一个合法的文档:
1.一个空的字符串是一个合法的文档。
2.如果A,B都是合法的文档,那么AB也是合法的文档。
3.如果S是合法的文档,那么aSb也是合法的文档,其中a,b是同一种括号,并且a是左括号,b是右括号。
现在给出q个询问,每次询问只考虑文档第l至r个字符的情况下,文档是不是合法的。
输入描述:
第一行两个整数n,m,q(1 ≤ n,m,q ≤ 106)。
第二行有n个空格隔开的整数x,第i个整数xi(0 ≤ xi < m*2)代表文档中的第i个字符是第种括号。另外,如果xi是偶数,它代表一个左括号,否则它代表一个右括号。
接下来q行,每行两个空格隔开的整数l,r(1 ≤ l ≤ r ≤ n),代表询问第l至r个字符构成的字符串是否是一个合法的文档。
输出描述:
输出共q行,如果询问的字符串是一个合法的文档,输出"Yes",否则输出"No"。
示例1
输入
6 4 3
0 2 3 1 4 7
1 4
1 5
5 6
输出
Yes
No
No

分析: 对于任意一个区间 [ l e , r i ] [le, ri] [le,ri]来说,要想是括号匹配的,我们可以得到几个充要条件:

  1. r i − l e + 1 ri - le + 1 rile+1 要是偶数
  2. 一半的左括号,一半的右括号
  3. 每个左括号与之相匹配的右括号一定在这个区间中(每个括号如果匹配,有且只有一个括号与之匹配),如果没有匹配的我们定义为INF就好.

只要满足以上条件,那么这个区间一定是可以完全匹配.
我们一个个解决上述问题,首先是个数问题,我们可以打两个前缀表就可以解决. 对于匹配位置,我们可以提前o(n)栈扫一遍得到每个括号匹配的括号位置. 之后在建立线段树,每次查询当前区间里面匹配位置的最大值,只要这个最大值在区间中就好.
Code

 
#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <stack>
#include <vector>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define rep(i, l, r) for(int i = l; i < r; i++)
#define per(i, r, l) for(int i = r; i >= l; i--)
#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<"\n"


typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;

const int N = (int) 1e6 + 11;
const int M = (int) 1e6 + 11;
const int MOD = (int) 1e9 + 7;
const int INF = (int) 0x3f3f3f3f;
const ll INFF = (ll) 0x3f3f3f3f3f3f3f3f;
/*-----------------------------------------------------------*/
pii type[N];
int reflect[N];
int sumleft[N], sumright[N];

int mx[N << 2];
void build(int rt, int L, int R){
	if(L == R){
		mx[rt] = reflect[L];
		return ;
	}
	int mid = (L + R) >> 1;
	build(rt << 1, L, mid);
	build(rt << 1 | 1, mid + 1, R);
	mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
int query(int rt, int L, int R, int le, int ri){
	if(le <= L && R <= ri){
		return mx[rt];
	}
	int mid = (L + R) >> 1;
	if(ri <= mid) return query(rt << 1, L, mid, le, ri);
	else if(le > mid) return query(rt << 1 | 1, mid + 1, R, le, ri);
	else  return max(query(rt << 1, L, mid, le, mid), query(rt << 1 | 1, mid + 1, R, mid + 1, ri));
}
int main(){
	int n, m, q; scanf("%d%d%d", &n, &m, &q);
	sumleft[0] = sumright[0] = 0;
	rep(i, 1, n + 1) {
		scanf("%d" ,&type[i].first);
		type[i].second = i;
		sumleft[i] = sumleft[i - 1] + (type[i].first & 1);
		sumright[i] = sumright[i - 1] + !(type[i].first & 1);
		reflect[i] = INF;
	}

	stack<pii>s;
	rep(i, 1, n + 1){
		if(s.empty()) s.push(type[i]);
		else {
			if(!(s.top().first & 1) && (type[i].first & 1) && s.top().first == (type[i].first - 1) ) {
				reflect[s.top().second] = i;
				reflect[i] = s.top().second;
				s.pop();
			}else s.push(type[i]);
		}
	}
	
//	rep(i, 1, n + 1) 	cout << i << " " << reflect[i] << "\n"; 

	build(1, 1, n);
	while(q--){
		static int l, r; scanf("%d%d", &l, &r);
		bool flag = 0;
		if((r - l + 1) % 2 == 0 && (sumleft[r] - sumleft[l - 1]) == (r - l + 1) / 2 && (sumleft[r] - sumleft[l - 1]) == (sumright[r] - sumright[l - 1])) {
			if(query(1, 1, n, l, r) <= r) flag = 1;
//			dbg(l); dbg(r); dbg(query(1, 1, n, l, r));
		}
		puts(flag ? "Yes" : "No");
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值