A. Function Height
在一个坐标轴上, 给一个n,在2n+1长度的轴上,允许调高任意一个奇数的点的高度,形成一个三角形,每个三角形会框出一个区域。所有跳高过的点的最高点,称为坐标最高点。求,给定n,要圈出k面积的区域,求怎么调整每个点的高度,使得最高点最小。
嗯。。。说实话题目挺绕的,但是其实非常简单。。。不管如何调整高度,框出来的三角形面积总是跟高度相同,因为:
S = 2 * h / 2 = h
然后要圈出k面积,只需要取 k/n 的高度就行了。这个是数学形式上的最小最高点。但是由于题目要求整数,那就向上取整一下,就ok了。
#include "bits/stdc++.h"
using namespace std;
int main(int argc, char *argv[])
{
long long n,k;
cin >> n >> k;
long long ret = k / n;
if(ret * n != k)
{
ret ++;
}
cout << ret;
return 0;
}
B. Diagonal Walking v.2
这个题目当时脑子抽了,没有想明白,一直WA,等做完了,看看别人的思路,才明白,这么简单。
总的来说,思路就是,结果只有三种情况,k, k -1, k-2。
为什么呢?
首先我们来看下行走策略。
1,直接斜着走。嗯,这个当然很好
2,如果同一个方向走超过两个单位,那么,实际上可以变成两个斜线走。比如,从(0,0)走到(0,2),可以这样走。先走到(1,1),然后再走到(0,2)。
3,如果同一个方向走一个单位。嗯,没法了,只能直线走。
4,还有一种,为了走够k步,不得不走直线,浪费步数,比如k为3,目标为(0,0),那么必须拿出两部来走直线。即(1,1) (1,0) (0,0)的走法。
5,唯一一种不能到达的,就是k < m + n了。
所以总体策略很简单。我们这么做:
1,先走斜线,走min(n,m)步,此时跟n或者m里面一个持水平
2,走直线,走max(n,m) - min(n,m)步,用斜线走,此时应该到(m,n)附近或者差1一步。
3,如果m+n为奇数,可以猜测,要么min(n,m)是奇数,要么max(n,m) 是奇数。但是无论如何,max(m,n)-min(m,n)一定是奇数。所以走直线这里,必定需要有一步直线,回到(m,n)。所以如果m+n为奇数,最后的答案就是k-1
4,如果m+n为偶数,要么刚好走到(m,n),剩余的步数为偶数。那就k步都可以走斜线。
5,要么走到(m,n)的时候还多出一步。那这个时候只能拿出两个直线,来消耗这个多出来的斜线了。这个时候就只有k-2步可以走斜线了。而这种情况的判定,可以看第二个步骤,走直线那里。如果max(m,n) - min(m,n)为奇数,此时说明直线不能通过走两步斜线刚好到达,需要多两步。
#include "bits/stdc++.h"
using namespace std;
void debug(std::string p)
{
cout << p;
}
int main(int argc, char *argv[])
{
int q = 0;
cin >> q;
long long n, m, k;
while(q--)
{
cin >> n >> m >> k;
long long ret;
if(n > k || m > k)
{
cout << -1 << endl;
continue;
}
if((m + n) % 2 == 0)
{
//
ret = k;
if((2 + std::llabs(m - k)) % 2 == 1)
{
ret -= 2;
}
}
else
{
ret = k - 1;
}
cout << ret << endl;
}
return 0;
}
C. Classy Numbers
求[l,r]范围内,有多少数字满足只有三个非零数字位的条件。如12000,10001都满足。但是1111不满足。
把问题分两部分看,分别求小于 l + 1和小于r的范围内,有多少个数字满足要求,然后相减即可。这就转换为如何求小于数字x的数字数量了。
这个计数方法大致就是:
1,从高位开始,先选第一个数字n,定下来,这个数字非零。
2,剩下的几位,选0,1,2位非零数字,在里面填数字。
3,最高位变动,其变动范围为1到n-1,每个为定好之后,按照2处理。
4,重复第1为,但是这次定下的是第一第二位。
#include "bits/stdc++.h"
#include <cmath>
using namespace std;
long long C(long long n,long long m){
long long ret = 1l;
for(long long i = 0l; i < m; i ++)
{
ret *= (n - i);
}
for(long long i = 0l; i < m; i ++)
{
ret /= (i + 1);
}
return ret;
}
long long my_pow(long long a, long long b)
{
if(b == 1)
{
return a;
}
else if(b == 0)
{
return 1;
}
long long halRet = my_pow(a, b/2);
if(b % 2 == 1)
{
return halRet * halRet * a;
}
else
{
return halRet * halRet;
}
}
long long f(long long x)
{
stringstream ss;
ss << x;
string s;
ss >> s;
reverse(s.begin(),s.end());
long long ret = 0, nonzero = 0;
for(int i = s.length() - 1; i >= 0; i--)
{
int d = s[i] - '0';
if(d != 0)
{
for(int k = 0; k < (4 - nonzero); k ++)
{
ret += C(i,k) * my_pow(9, k);
}
nonzero++;
for(int k = 0; k < (4 - nonzero); k ++)
{
ret += (d - 1) * C(i,k) * my_pow(9, k);
}
}
if(nonzero > 3)
{
break;
}
}
return ret;
}
int main(int argc, char *argv[])
{
long long q = 0;
cin >> q;
while(q--)
{
long long l, r;
cin >> l >> r;
cout << f(r + 1) - f(l) << endl;
}
return 0;
}
D. Vasya and Arrays
给定两个数组,允许合并连续的数字。两个数字分别合并,合并完之后,让两个数组尽可能长。
由于所有输入的数字都是正整数,且合并的时候不能移动位置。所以可以用贪心算法,轮流合并即可。
#include "bits/stdc++.h"
using namespace std;
long long a[300005];
long long b[300005];
long long c[300005];
int main(int argc, char *argv[])
{
int na, nb;
long long sum = 0;
cin >> na;
for (int i = 0; i < na; ++i) {
scanf("%d", &a[i]);
sum += a[i];
}
cin >> nb;
for (int i = 0; i < nb; ++i) {
scanf("%d", &b[i]);
sum -= b[i];
}
if(sum != 0)
{
cout << -1;
return 0;
}
int ret = 0;
long long sa = a[0], sb =b[0];
int i,j;
for(i = 1, j = 1; i <= na && j <= nb;)
{
while(sa != sb && i <= na && j <= nb)
{
if(sa < sb)
{
sa += a[i];
i++;
}
else
{
sb += b[j];
j++;
}
}
if(sa == sb)
{
c[ret] = sa;
ret++;
sa = sb = 0;
if(i >= na || j >= nb)
{
break;
}
sa = a[i];
sb = b[j];
i++;
j++;
}
}
if(i == na && j == nb)
{
cout << ret << endl;
}
else
{
cout << -1 << endl;
}
return 0;
}
E. Covered Points
给定很多线段。求这些线段覆盖到的整数点。这些线段会有交错,但是肯定不在一条线上。
先说第一部分。求一根线覆盖了多少整数点。这个点数很简单,__gcd(abs(dx), abs(dy)) + 1就是了。为什么?
令gcd(abs(dx),abs(dy))为gcd,必然有gcd a = abs(dx), gcd b = abs(dy),a,b都为整数。此时,x每前进a长度,y必定前b长度。一共可以前进gcd次。
然后,就是求交错的整数点的数量了。这个,就是求交线,然后确认是不是整数,然后看看是不是在线段内,就ok了。这个套个模版,就ok。
#include<bits/stdc++.h>
using namespace std;
#define N 1005
typedef struct _point
{
long x;
long y;
}point;
struct seg{
int x1, y1, x2, y2;
seg(){};
};
struct line{
long long A, B, C;
line(){};
line(seg a){
A = a.y1 - a.y2;
B = a.x2 - a.x1;
C = -A * a.x1 - B * a.y1;
};
};
long long det(long long a, long long b, long long c, long long d){
return a * d - b * c;
}
//求两个线是否有交集
bool inter_for_line(seg a, seg b, int& x, int& y){
line l1(a), l2(b);
long long dx = det(l1.C, l1.B, l2.C, l2.B);
long long dy = det(l1.A, l1.C, l2.A, l2.C);
long long d = det(l1.A, l1.B, l2.A, l2.B);
if (d == 0)
return false;
if (dx % d != 0 || dy % d != 0)
return false;
x = -dx / d;
y = -dy / d;
return true;
}
bool in_seg(int x, int l, int r){
if (l > r) swap(l, r);
return (l <= x && x <= r);
}
//求两个线段是否有交集
bool inter_for_seg(seg a, seg b, int& x, int& y)
{
if(inter_for_line(a,b,x,y))
{
if (!in_seg(x, a.x1, a.x2) || !in_seg(y, a.y1, a.y2))
return false;
if (!in_seg(x, b.x1, b.x2) || !in_seg(y, b.y1, b.y2))
return false;
return true;
}
return false;
}
//求线段内的整数坐标数
int get_int_coordinate_num(seg a){
int dx = a.x1 - a.x2;
int dy = a.y1 - a.y2;
if(dx == 0)
{
return abs(dy) + 1;
}
else if(dy == 0)
{
return abs(dx) + 1;
}
return __gcd(abs(dx), abs(dy)) + 1;
}
int main(int argc, char *argv[])
{
int n = 0;
int x1,y1,x2,y2;
vector<seg> a;
seg tmp;
cin >> n;
for (int i = 0; i < n; ++i)
{
scanf("%d %d %d %d", &x1,&y1, &x2,&y2);
tmp.x1 = x1;
tmp.x2 = x2;
tmp.y1 = y1;
tmp.y2 = y2;
a.push_back(tmp);
}
long long cnt = 0;
for (int i = 0; i < n; ++i)
{
set<pair<int, int> > pts;
cnt += get_int_coordinate_num(a[i]);
for(int j = 0; j < i; j++)
{
if(inter_for_seg(a[i], a[j], x1,y1))
{
pts.insert(pair<int, int>(x1,y1));
}
}
cnt -= pts.size();
}
cout << cnt << endl;
return 0;
}
G. Sources and Sinks
有n个点,m个有向线段构成有向图。然后他们最多只有20个source和sink,source就是入度为0,sink是出度为0。source和sink数量保证相同。求,以任意方式从sink添加一个线段到source,是不是可以构成强联通。
题目看起来点线很多,但是,由于是有向图,所以可以肯定,对于非source和sink的点,必定至少有一个source点能到达该点,并且该点一定有一个方法,能到达sink点。
所以题目缩减到了40个source和sink点的问题。
更进一步,什么时候会有非强联通图呢?那就是有一个子图,这个子图内,所有的sink,都连接到本子图source。此时,本土的source,必定会有某些source不能到达(当然,还有可能有其他点,但是source点肯定不能到达)。
所以,只要枚举了所有可能,没有这种自图,就能说yes了。
这里还TLE了几次,分别是因为用了map来组织g数组,以及用cin来读输入。事实证明O(logn)和O(1)还是有差异的。。。。同时也证明自己偷懒就得浪费运行时时间的真理。。。
#include "bits/stdc++.h"
using namespace std;
int input[1000005];
int output[1000005];
bool reachable[21][1000005];
vector<int> tail;
// use an array will be much faster then map
std::vector<int> g[1000005];
void dfs(int sourceNumber, int source)
{
reachable[sourceNumber][source] = true;
for(auto x:g[source])
{
if(reachable[sourceNumber][x] == false)
{
dfs(sourceNumber, x);
}
}
}
int main(int argc, char *argv[])
{
int n,m;
int from, to;
scanf("%d %d", &n, &m);
memset(input,0, sizeof(input));
memset(output,0, sizeof(output));
memset(reachable, 0 , sizeof(reachable));
tail.clear();
for (int i = 0; i < m; ++i) {
//scanf is faster than cin!
scanf("%d %d", &from, &to);
g[from].push_back(to);
input[to]++;
output[from]++;
}
int sourceNumber = 0;
for (int i = 1; i <= n; ++i) {
if(input[i] == 0)
{
dfs(sourceNumber, i);
sourceNumber++;
}
if(output[i] == 0)
{
tail.push_back(i);
}
}
set<int> s;
for(int i = 1; i < (1 << sourceNumber) - 1; i ++)
{
s.clear();
int cnt = 0;
for (int j = 0; j < sourceNumber; ++j) {
if(i & ( 1<<j))
{
//chech this source
cnt++;
for(auto x:tail)
{
if(reachable[j][x] == true)
{
s.insert(x);
}
}
}
}
if(s.size() <= cnt)
{
cout << "NO" << endl;
return 0;
}
}
cout << "YES" << endl;
return 0;
}