Problem A - Duplicate Pair FZU - 1001
难度:★☆☆☆☆
定位:数组遍历签到题
题目大意:输入1~n-1,共有n个数,其中有且仅有一个重复的数字,找到其并输出。注意多组样例!
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1000000 + 10;
bool mark[maxn];
int main()
{
ios::sync_with_stdio(false);
int n;
while (cin >> n){
memset(mark, 0, sizeof(mark));
for (int i = 0, x; i < n; i ++){
cin >> x;
if (mark[x]){
cout << x << endl;
}
mark[x] = true;
}
}
return 0;
}
Problem B - Hulk CodeForces - 705A
难度:★☆☆☆☆
定位:字符串输出签到题
题目大意:轮流输出“I hate”,“I love”,不过要注意中间的间隔词汇变化,以及空格的处理。
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
int cnt = 0;
while (n --){
if (cnt) cout << "that ";
if (cnt & 1) cout << "I love ";
else cout << "I hate ";
cnt ++;
}
cout << "it" << endl;
return 0;
}
Problem C - A Math Problem HDU - 6182
难度:★☆☆☆☆
定位:快速幂(或者细心的朋友可能发现,可以使用预处理暴力过去)
题目大意:问在正整数中,有多少个正整数“k”的k次方,是小于等于n的
解决办法:
- 方法一,快速幂,防止多次循环求幂超时,注意16的本身次幂会溢出,这是一个坑点;
- 方法二,预处理出来15以及15之前所有正整数的本身次方,存储在数组中进行暴力即可,因为16的本身次幂已经超过了10^18大小。
#include <iostream>
using namespace std;
#define LL long long
LL pow_mod(LL n, LL a){
LL ans = 1;
while (a){
if (a & 1){
ans *= n;
}
n *= n;
a >>= 1;
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
LL n;
while (cin >> n){
int ans = 0;
LL base = 1;
while (pow_mod(base, base) <= n && base <= 15){
ans ++;
base ++;
}
cout << ans << endl;
}
return 0;
}
Problem D - Cable master POJ - 1064
难度:★★☆☆☆
定位:二分枚举上限
题目大意:电缆提供商提供了n条电缆,但是为了保证比赛的公正性,现在你要将他们切割成k段长的相等的电缆。可以浪费,不过不可以拼接,问每段最大的长度是多少。
解决办法:看似题目冗长,实则在阅读理解背后的算法原理很简单。二分枚举切割后每段的长度,如果能够切割足够的k段,那么就向上收缩二分上界,否则向下收缩二分上界。这样可以大大降低复杂度,这也是一道求“多种不同值中找最大相同值”的非常经典的二分枚举上界的题目。
#include <iostream>
#include <iomanip>
#include <cstdio>
using namespace std;
const int maxn = 10000 + 10;
double a[maxn];
int n, k;
bool ok(double x){
int cnt = 0;
for (int i = 0; i < n; i ++){
cnt += (int)(a[i] / x);
}
return cnt >= k;
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++){
scanf("%lf", &a[i]);
}
double l = 0.0, r = (double)(1e8);
double mid;
int cnt = 0;
while (cnt < 100){
mid = (l+r) / 2.0;
if (ok(mid)){
l = mid;
}
else {
r = mid;
}
cnt ++;
}
cout << setprecision(2) << fixed << ((double)((int)(mid*100))) / 100.0 << endl;
return 0;
}
Problem E - Barcelonian Distance CodeForces - 1078A
难度:★★☆☆☆
定位:分类讨论
题目大意:给出一个在二维平面直角坐标系第一象限内的,单位长度为1的无限大网格,每条直线都代表道路。又给你一条直线 ax+by+c=0,也代表一条道路。求两点之间的最短距离。
解决办法:找到以两个点连线为对角线的矩形,并将其四条边无限延长,跟另外一条直线,会产生两个(直线水平或竖直)或者四个交点(直线倾斜),计算出这个些点,然后算出所有可能的走法的长度即可。注意题目精度为1e-10。
#include <iostream>
#include <cmath>
#include <cstdio>
#include <iomanip>
using namespace std;
#define eps 1e-10
#define sq(x) ((x)*(x))
struct Point {
double x, y;
Point() {}
Point(double x, double y): x(x), y(y) {}
} p1, p2, q1, q2, q3, q4;
inline double dis(const Point& a, const Point& b) {
return sqrt(sq(a.x-b.x) + sq(a.y-b.y));
}
inline double min(double a, double b, double c, double d, double e){
double ans = a;
ans = min(ans, b);
ans = min(ans, c);
ans = min(ans, d);
ans = min(ans, e);
return ans;
}
int main()
{
double a, b, c;
double ans, ans1, ans2, ans3, ans4;
ans = ans1 = ans2 = ans3 = ans4 = (double)(1e12);
cin >> a >> b >> c >> p1.x >> p1.y >> p2.x >> p2.y;
ans = fabs(p1.x-p2.x) + fabs(p1.y-p2.y);
if (fabs(a) < eps){
q1 = Point(p1.x, -c/b);
q2 = Point(p2.x, -c/b);
ans1 = dis(p1,q1) + dis(q1,q2) + dis(q2,p2);
}
else if (fabs(b) < eps){
q1 = Point(-c/a, p1.y);
q2 = Point(-c/a, p2.y);
ans2 = fabs(p1.x-q1.x) + dis(q1,q2) + fabs(p2.x-q2.x);
}
else {
q1 = Point(p1.x, -(a*p1.x+c)/b);
q3 = Point(-(b*p1.y+c)/a, p1.y);
q2 = Point(p2.x, -(a*p2.x+c)/b);
q4 = Point(-(b*p2.y+c)/a, p2.y);
ans1 = dis(p1,q1) + dis(q1,q2) + dis(q2,p2);
ans2 = dis(p1,q1) + dis(q1,q4) + dis(q4,p2);
ans3 = dis(p1,q3) + dis(q3,q2) + dis(q2,p2);
ans4 = dis(p1,q3) + dis(q3,q4) + dis(q4,p2);
}
cout << setprecision(10) << fixed << min(ans,ans1,ans2,ans3,ans4) << endl;
}
Problem F - Segments POJ - 3304
难度:★★☆☆☆
定位:思维+计算几何直线与线段相交
题目大意:给出n条线段,问是否能找到一条直线,使所有n条线段在此条直线上的投影至少有一个公共点。
解法:注意开动脑筋,拥有一个公共点,那么就代表着,以这个公共点为垂足,做投影所在直线的垂直线,那么这条垂直线必然和n条线段都相交了。现在题目就转化为了,能不能找到一条直线,与所有线段都相交。枚举任意两个端点,判断二者构成的直线与n条线段是否相交即可完成此题目。
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
#define eps 1e-8
#define sq(x) ((x)*(x))
const int maxn = 100 + 10;
int n;
struct Point {
double x, y;
Point() {}
Point(double x, double y): x(x), y(y) {}
} pp[maxn * 2];
struct Seg {
Point p1, p2;
} a[110];
Point operator - (Point p, Point q) {
return Point(p.x-q.x, p.y-q.y);
}
double dist(Point v1, Point v2){
return sqrt(sq(v1.x-v2.x) + sq(v1.y-v2.y));
}
double cross(const Point & v1, const Point & v2) {
return v1.x * v2.y - v1.y * v2.x;
}
bool ok(Point p1, Point p2) {
if(dist(p1, p2) < eps) return false;
else {
for(int i = 0; i < n; i++) {
if (cross(p2 - p1, a[i].p1 - p1)*cross(p2 - p1, a[i].p2 - p1) > eps){
return false;
}
}
}
return true;
}
int main() {
int tt;
scanf("%d", &tt);
while (tt --) {
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lf%lf%lf%lf", &a[i].p1.x, &a[i].p1.y, &a[i].p2.x, &a[i].p2.y);
int ans = 0;
if (n == 1) {
ans = 1;
}
else {
for(int i = 0; i < n; i ++){
for(int j = i+1; j < n; j ++) {
if (ok(a[i].p1, a[j].p1) ||
ok(a[i].p1, a[j].p2) ||
ok(a[i].p2, a[j].p1) ||
ok(a[i].p2, a[j].p2)){
ans = 1;
}
if (ans) break;
}
}
}
ans ? printf("Yes!\n"):
printf("No!\n");
}
return 0;
}
Problem G - Keywords Search HDU - 2222
难度:★★★☆☆
定位:AC自动机模板题目
题目大意:给出n个被查询词汇模板串,最后再给出1个查询文本串,问这些被查询的模板串中,有多少个出现在了文本串中。
解法:AC自动机,裸板子。注意,模板串可能会出现重复,所以这里我们需要利用val数组进行标记出现次数,具体方法如下。
坑点:用KMP会超时,且AC自动机的数组大小不能开的过大,否则会MLE。建议学习AC自动机之前,去练习Trie树的相关知识,因为AC自动机的串存储结构是以Trie树为基础的。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 500000 + 10;
struct node{
int child[maxn][26 + 10];
int fail[maxn];
int val[maxn];
int size;
node(){
memset(child, 0, sizeof(child));
memset(fail, 0, sizeof(fail));
memset(val, 0, sizeof(val));
size = 1;
}
void init(){
memset(child, 0, sizeof(child));
memset(fail, 0, sizeof(fail));
memset(val, 0, sizeof(val));
size = 1;
}
inline int idx(char c){
return c - 'a';
}
void insert(string s){
int n = s.size();
int u = 0;
for (int i = 0; i < n; i ++){
int c = idx(s[i]);
if (!child[u][c]){
memset(child[size], 0, sizeof(child[size]));
val[size] = 0;
child[u][c] = size ++;
}
u = child[u][c];
}
val[u] ++;
}
int find(string t){
int ans = 0;
int n = t.size();
int j = 0;
for (int i = 0; i < n; i ++){
int c = idx(t[i]);
while (j && !child[j][c]) j = fail[j];
j = child[j][c];
int jj = j;
while (val[jj]){
ans += val[jj];
val[jj] = 0;
jj = fail[jj];
}
}
return ans;
}
void getFail(){
fail[0] = 0;
queue<int> qq;
for (int i = 0; i < 26; i ++){
if (!child[0][i]) continue;
else {
fail[child[0][i]] = 0;
qq.push(child[0][i]);
}
}
while (!qq.empty()){
int u = qq.front();
qq.pop();
for (int i = 0; i < 26; i ++){
if (!child[u][i]) continue;
else {
qq.push(child[u][i]);
int v = child[u][i];
int j = fail[u];
while (j && !child[j][i]) j = fail[j];
fail[v] = child[j][i];
}
}
}
}
} ACM;
int main()
{
ios::sync_with_stdio(false);
int tt;
cin >> tt;
int n;
while (tt --){
ACM.init();
string s, t;
cin >> n;
for (int i = 0; i < n; i ++){
cin >> s;
ACM.insert(s);
}
ACM.getFail();
cin >> t;
cout << ACM.find(t) << endl;
}
return 0;
}