HDU-4622 Reincarnation

题意:给定一个字符串,有Q次询问,每次询问得出区间[L, R]内有多少个不同的子串。

分析:后缀数组搞,不过hash+dp也能够搞定这题,详解见http://www.cnblogs.com/Lyush/p/3233573.html

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
using namespace std; const int N = ;
char str[N];
int len, seq[N];
int sa[N], rank[N], height[N];
int wa[N], wb[N], ws[N], wv[N];
int f[N][];
int lg[N]; bool cmp(int r[], int a, int b, int l) {
return r[a] == r[b] && r[a+l] == r[b+l];
} void da(int r[], int sa[], int n, int m) {
int i, j, p, *x = wa, *y = wb;
for (i = ; i < m; ++i) ws[i] = ;
for (i = ; i < n; ++i) ws[x[i]=r[i]]++;
for (i = ; i < m; ++i) ws[i] += ws[i-];
for (i = n-; i >= ; --i) sa[--ws[x[i]]] = i;
for (j = , p = ; p < n; j *= , m = p) {
for (p = , i = n - j; i < n; ++i) y[p++] = i;
for (i = ; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = ; i < n; ++i) wv[i] = x[y[i]];
for (i = ; i < m; ++i) ws[i] = ;
for (i = ; i < n; ++i) ws[wv[i]]++;
for (i = ; i < m; ++i) ws[i] += ws[i-];
for (i = n-; i >= ; --i) sa[--ws[wv[i]]] = y[i];
for (swap(x, y), p = , x[sa[]] = , i = ; i < n; ++i)
x[sa[i]] = cmp(y, sa[i-], sa[i], j) ? p- : p++;
}
} void calheight(int r[], int sa[], int n) {
int i, j, k = ;
for (i = ; i <= n; ++i) rank[sa[i]] = i;
for (i = ; i < n; height[rank[i++]] = k)
for (k?k--:, j = sa[rank[i]-]; r[i+k] == r[j+k]; ++k) ;
} void initrmq() {
int LIM = (int)log2(1.0*N);
for (int i = ; i <= len; ++i) {
f[i][] = height[i];
}
for (int j = ; j <= LIM; ++j) {
for (int i = ; i+(<<j)- <= len; ++i) {
f[i][j] = min(f[i][j-], f[i+(<<j-)][j-]);
}
}
} int query(int l, int r) {
int k = lg[r-l+];
return min(f[l][k], f[r-(<<k)+][k]);
} int cal(int l, int r) {
int ret = (r-l+)*(r-l+)/, last = -;
int lcp, a, b, alen, blen, k = r-l+;
for (int i = ; i <= len && k; ++i) {
if (sa[i] >= l && sa[i] <= r) { // 说明该后缀位于所选的区间内,且全局rank最靠前
k--;
if (last != -) {
a = last, b = i;
if (a > b) swap(a, b);
lcp = query(a+, b);
alen = r-sa[last]+, blen = r-sa[i]+;
/* if (lcp < alen && lcp < blen) {last = i;}
else if (lcp >= alen && lcp >= blen) {
if (blen > alen) last = i;
}
else if (lcp >= alen) {last = i;}
*/
if (alen > blen && lcp >= blen) {}
else last = i;
ret -= min(lcp, min(alen, blen));
} else last = i;
}
}
return ret;
} void solve() {
int Q, l, r;
scanf("%d", &Q);
while (Q--) {
scanf("%d %d", &l, &r);
printf("%d\n", cal(l-, r-)); // 字符串编号从0开始
}
} int main() {
lg[] = -;
for (int i = ; i < N; ++i) {
lg[i] = lg[i>>] + ;
}
int T;
scanf("%d", &T);
while (T--) {
scanf("%s", str);
len = strlen(str);
for (int i = ; i < len; ++i) seq[i] = str[i]-'a'+;
seq[len] = ;
da(seq, sa, len+, );
calheight(seq, sa, len);
initrmq();
solve();
}
return ;
}

HDU-4630 No Pain No Game

题意:给出1-N的全排列,后面跟随Q次询问,每次询问给出L, R,求给出的排列中[L, R]中最大的一对最大公约数时多少?

分析:比赛的时候觉得这题怎么搞都没有办法把时间复杂度降下来,知道是离线处理的方式但是不知道怎么搞。其实以后遇到这种要离线的题目先设想一种能够从一端求解到另一端的算法,即算法维护一个前 i 个元素的一个性质,即这个性质只适合从前往后计算过去。。。。。还是要多题目这类离线的题目。针对该题而言,首先把1-50000每个数的所有因子全部保存起来,将所有询问排序,然后从数组的1号元素开始往后遍历,每次扫描该数的所有因子,查看是否有因子x在前面出现过,如果出现过,假设最靠右的位置为last[x]那么就更新1-last[x]的最大值为x(只是试探性的覆盖,只覆盖比它小的,不覆盖本来就比其大的值),表示如果后面的询问如果左端点如果在1-last[x]内的话,那么最大公约数至少为x。注意如果一个数被处理,那么它的所有因子的last[]就赋值成当前位置,因此该数也能够提供这个因子。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
#define lch p<<1
#define rch p<<1|1
using namespace std; const int N = ; struct SegmentTree {
struct Node {
int lazy;
int l, r, Max;
};
Node e[N<<];
void push_up(int p) {
e[p].Max = max(e[lch].Max, e[rch].Max);
}
void push_down(int p) {
if (e[p].lazy != -) {
e[lch].lazy = max(e[lch].lazy, e[p].lazy);
e[rch].lazy = max(e[rch].lazy, e[p].lazy);
e[p].Max = max(e[p].Max, e[p].lazy);
}
}
void build(int p, int l, int r) {
e[p].l = l, e[p].r = r, e[p].lazy = -;
if (l != r) {
int mid = (l + r) >> ;
build(lch, l, mid);
build(rch, mid+, r);
push_up(p);
} else {
e[p].Max = ;
}
}
void modify(int p, int l, int r, int val) {
if (e[p].l == l && e[p].r == r) {
e[p].lazy = max(e[p].lazy, val);
if (l == r) {
e[p].Max = max(e[p].Max, e[p].lazy);
e[p].lazy = -;
}
return;
}
push_down(p);
int mid = (e[p].l + e[p].r) >> ;
if (r <= mid) modify(lch, l, r, val);
else if (l > mid) modify(rch, l, r, val);
else modify(lch, l, mid, val), modify(rch, mid+, r, val);
push_up(p); // 更新下去的才需要push_up
}
int query(int p, int l, int r) {
if (e[p].l == l && e[p].r == r) {
if (l == r) {
e[p].Max = max(e[p].Max, e[p].lazy);
e[p].lazy = -;
}
return e[p].Max;
}
push_down(p);
int mid = (e[p].l + e[p].r) >> ;
if (r <= mid) return query(lch, l, r);
else if (l > mid) return query(rch, l, r);
else return max(query(lch, l, mid), query(rch, mid+, r));
}
}; struct Query {
int l, r, No;
bool operator < (const Query &t) const {
return r < t.r;
}
}q[N]; int n, Q, seq[N];
int last[N], ans[N];
vector<int>v[N];
SegmentTree st; void prepare() {
for (int i = ; i <= ; ++i) {
for (int j = i; j <= ; j+=i) {
v[j].push_back(i);
}
}
} void solve() {
int p = ;
memset(last, 0xff, sizeof (last));
st.build(, , n);
for (int i = ; i <= n; ++i) {
vector<int>&vt = v[seq[i]];
for (int j = ; j < (int)vt.size(); ++j) { // 读取该数字的所有因子进行更新
if (last[vt[j]] != -) { // 说明前面出现过
st.modify(, , last[vt[j]], vt[j]);
}
last[vt[j]] = i;
}
while (p <= Q && q[p].r == i) {
if (q[p].r == q[p].l) ans[q[p].No] = ;
else ans[q[p].No] = st.query(, q[p].l, q[p].l);
++p;
}
if (p > Q) break;
}
for (int i = ; i <= Q; ++i) {
printf("%d\n", ans[i]);
}
} int main() {
prepare();
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = ; i <= n; ++i) {
scanf("%d", &seq[i]);
}
scanf("%d", &Q);
for (int i = ; i <= Q; ++i) {
scanf("%d %d", &q[i].l, &q[i].r);
q[i].No = i;
}
sort(q+, q++Q);
solve();
}
return ;
}

HDU-4631 Sad Love Story

题意:题目中给出了六个参数:Ax,Bx,Cx,Ay,By,Cy,使用三个参数能够生成一个序列,这个序列:ai = (A * ai-1 + B) % C。现在初始化平面二维坐标点为(0, 0),x坐标和y坐标都按照这个规律进行生成。第一个点对就是(Bx%Cx, By%Cy)。现在问在t=2时刻起,每一时刻平面上的最近点对的距离的平方总和为多少?

分析:由于题目中反复强调了题目所给定的数据都是随机的,六个参数也是随机的,因此一种解法是使用set逐个进行加点(按照x轴进行排序),每次加点都只需要访问当前这个点周围的点即可。具体的范围是指周围的点与当前点的横坐标的距离平方不超过最近点对距离平方。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#include <ctime>
using namespace std; typedef long long LL;
struct Point {
int x, y;
Point() {}
Point(int _x, int _y) : x(_x), y(_y) {}
bool operator < (const Point &other) const {
if (x != other.x) return x < other.x;
else return y < other.y;
}
};
int n;
int fac[];
set<Point>st; LL dist(const Point &a, const Point &b) {
return 1LL*(a.x-b.x)*(a.x-b.x)+1LL*(a.y-b.y)*(a.y-b.y);
} int gen_xy(int f[], int xy) {
return (1LL*xy*f[]+f[])%f[];
} void cal(const Point &obj, LL &close) {
LL d;
set<Point>::iterator it, tmp;
it = st.find(obj);
tmp = it, ++tmp;
while (tmp != st.end() && 1LL*(tmp->x - it->x)*(tmp->x - it->x) < close) {
d = dist(*it, *tmp);
if (d < close) {
close = d;
}
++tmp;
}
tmp = it, --tmp;
if (it == st.begin()) return;
while (1LL*(tmp->x - it->x)*(tmp->x - it->x) < close) {
d = dist(*it, *tmp);
if (d < close) {
close = d;
}
if (tmp == st.begin()) break;
--tmp;
}
} int main() {
int T;
scanf("%d", &T);
while (T--) {
LL ret = , close = (1LLU<<)-;
scanf("%d", &n);
st.clear();
for (int i = ; i < ; ++i) {
scanf("%d", &fac[i]);
}
int x = fac[]%fac[], y = fac[]%fac[];
st.insert(Point(x, y));
for (int i = ; i < n; ++i) {
x = gen_xy(fac, x);
y = gen_xy(fac+, y);
if (st.count(Point(x, y))) {
break;
}
st.insert(Point(x, y));
cal(Point(x, y), close);
ret += close;
}
printf("%I64d\n", ret);
}
return ;
}

另外一种想法就是使用求解平面最近点对的方法。想法是这样的:首先多所有的点集求解一次最近点对,然后得到两个点的编号(i, j),其中 i < j,那么对于编号较大的点 j,从 j+1 开始到最后加进来的点的最近点对都将会是(i, j),于是使用当前距离乘上区间长度然后将问题转化为求解(1, j-1)的最近点对问题。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std; struct Point {
int x, y, No;
Point() {}
Point(int _x, int _y, int _No) : x(_x), y(_y), No(_No) {}
};
bool operator < (const Point &a, const Point &b) {
if (a.x != b.x) return a.x < b.x;
return a.y < b.y;
} typedef long long LL;
const int N = ;
int n;
int fac[];
int rk[N];
Point pt[N], ptx[N]; inline bool cmpy(const int &a, const int &b) {
return ptx[a].y < ptx[b].y;
} int getxy(int z, int f[]) {
return (1LL*f[]*z+f[])%f[];
} LL dist(const Point &a, const Point &b) {
return 1LL*(a.x-b.x)*(a.x-b.x) + 1LL*(a.y-b.y)*(a.y-b.y);
} LL getpair(int l, int r, Point pnts[], int &p, int &q) { // p、q分别用来最近点对的编号
LL tmp, ret = ((unsigned long long)()<<)-;
if (l == r) return ret;
if (l + == r) { // 只剩下两个点时求最近点对
if (ret > (tmp=dist(pnts[l], pnts[l+]))) {
ret = tmp;
p = l, q = l + ;
}
return ret;
}
if (l + == r) { // 还剩下三个点
if (ret > (tmp=dist(pnts[l], pnts[l+]))) {
ret = tmp;
p = l, q = l + ;
}
if (ret > (tmp=dist(pnts[l], pnts[l+]))) {
ret = tmp;
p = l, q = l + ;
}
if (ret > (tmp=dist(pnts[l+], pnts[l+]))) {
ret = tmp;
p = l + , q = l + ;
}
return ret;
}
int mid = (l + r) >> ;
int pp, qq, idx = ;
if (ret > (tmp=getpair(l, mid, pnts, pp, qq))) {
ret = tmp;
p = pp, q = qq;
}
if (ret > (tmp=getpair(mid+, r, pnts, pp, qq))) {
ret = tmp;
p = pp, q = qq;
}
for (int i = l; i <= r; ++i) {
if (1LL*(pnts[i].x-pnts[mid].x)*(pnts[i].x-pnts[mid].x) < ret) {
rk[idx++] = i;
}
}
sort(rk, rk+idx, cmpy);
for (int c = ; c < idx; ++c) {
for (int d=c+; d < idx && 1LL*(pnts[rk[c]].y-pnts[rk[d]].y)*(pnts[rk[c]].y-pnts[rk[d]].y) < ret; ++d) {
if (ret > (tmp=dist(pnts[rk[c]], pnts[rk[d]]))) {
ret = tmp;
p = rk[c], q = rk[d];
}
}
}
return ret;
} void solve() {
int L = , R = n; // 初始化的左右计算区间
LL ret = ;
while (L < R) { // 最终会收敛与[1, 1]
int p, q;
memcpy(ptx+, pt+, sizeof(Point)*(R-L+));
sort(ptx+, ptx++(R-L+));
// 换成一个rank数组进行排序后,时长竟然还变成了,所以保留了这份
LL d = getpair(L, R, ptx, p, q);
if (ptx[p].No > ptx[q].No) swap(p, q);
ret += d*(R-ptx[q].No+);
R = ptx[q].No-;
}
printf("%I64d\n", ret);
} int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = ; i < ; ++i) {
scanf("%d", &fac[i]);
}
pt[] = Point(fac[]%fac[], fac[]%fac[], ); // 初始化第一个节点
for (int i = ; i <= n; ++i) {
pt[i] = Point(getxy(pt[i-].x, fac), getxy(pt[i-].y, fac+), i);
}
solve();
}
return ;
}

解题报告还说了KD树可做,但是我写的估计每个几十年跑不出来,每次插入一个点需要重建一棵树...... 贴个KD树的模板算了。

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std; typedef long long LL; inline int sign(double);
struct Point {
double x, y;
Point () {}
Point (double _x, double _y) : x(_x), y(_y) {}
void read() {
scanf("%lf %lf", &x, &y);
}
bool operator < (const Point &t) const {
if (sign(x-t.x) != ) return sign(x-t.x) < ;
return sign(y-t.y) < ;
}
}; const int N = ;
const double eps = 1e-;
int n, m, Div[N];
Point pt[N];
double ret; inline int sign(double x) {
return x < -eps ? - : x > eps;
} double dist(const Point &a, const Point &b) {
return sqrt(1.0*(a.x-b.x)*(a.x-b.x) + 1.0*(a.y-b.y)*(a.y-b.y));
} bool cmpX(const Point &a, const Point &b) {
return a.x < b.x;
} bool cmpY(const Point &a, const Point &b) {
return a.y < b.y;
} void BuildKD(int l, int r, Point p[]) {
if (l >= r) return; // 如果已经划分到叶子节点
int mid = l + r >> ;
double MinX, MaxX, MinY, MaxY;
MinX = min_element(p+l, p+r+, cmpX)->x;
MaxX = max_element(p+l, p+r+, cmpX)->x;
MinY = min_element(p+l, p+r+, cmpY)->y;
MaxY = max_element(p+l, p+r+, cmpY)->y;
Div[mid] = (MaxX - MinX) > (MaxY - MinY);
// Div[i]为0表示按照x轴划分,为1表示按照y轴划分,划分的依据仅仅是那个方向的取值跨度大
nth_element(p+l, p+mid, p+r+, Div[mid] ? cmpX : cmpY);
BuildKD(l, mid-, p);
BuildKD(mid+, r, p);
} void Find(int l, int r, const Point &a, Point p[]) {
if (l > r) return;
int mid = l + r >> ;
double dis = dist(a, p[mid]);
ret = min(ret, dis);
double d = Div[mid] ? (a.x - p[mid].x) : (a.y - p[mid].y);
int l1 = l, l2 = mid+;
int r1 = mid-, r2 = r;
if (sign(d) > ) swap(l1, l2), swap(r1, r2);
Find(l1, r1, a, p);
if (sign(ret - fabs(d)) > ) Find(l2, r2, a, p);
} int main() {
// n个节点,m次询问,返回离询问点最近的节点距离
while (scanf("%d %d", &n, &m) != EOF) {
for (int i = ; i < n; ++i) {
pt[i].read();
}
BuildKD(, n-, pt);
while (m--) {
Point p;
p.read();
ret = 1e30;
Find(, n-, p, pt);
printf("%f\n", ret);
}
}
return ;
}

2013 Multi-University Training Contest 3的更多相关文章

  1. Integer Partition(hdu4658)2013 Multi-University Training Contest 6 整数拆分二

    Integer Partition Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T ...

  2. Partition(hdu4651)2013 Multi-University Training Contest 5

    Partition Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  3. ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków

    ACM ICPC Central Europe Regional Contest 2013 Jagiellonian University Kraków Problem A: Rubik’s Rect ...

  4. Partition(hdu4651)2013 Multi-University Training Contest 5----(整数拆分一)

    Partition Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...

  5. JSU 2013 Summer Individual Ranking Contest - 5

    JSU 2013 Summer Individual Ranking Contest - 5 密码:本套题选题权归JSU所有,需要密码请联系(http://blog.csdn.net/yew1eb). ...

  6. HDU4888 Redraw Beautiful Drawings(2014 Multi-University Training Contest 3)

    Redraw Beautiful Drawings Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. HDU 2018 Multi-University Training Contest 3 Problem A. Ascending Rating 【单调队列优化】

    任意门:http://acm.hdu.edu.cn/showproblem.php?pid=6319 Problem A. Ascending Rating Time Limit: 10000/500 ...

  8. 2015 Multi-University Training Contest 8 hdu 5390 tree

    tree Time Limit: 8000ms Memory Limit: 262144KB This problem will be judged on HDU. Original ID: 5390 ...

  9. hdu 4946 2014 Multi-University Training Contest 8

    Area of Mushroom Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  10. 2016 Multi-University Training Contest 2 D. Differencia

    Differencia Time Limit: 10000/10000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Tot ...

随机推荐

  1. game of life

    class Solution { public: void gameOfLife(vector<vector<int>>& board) { queue<int& ...

  2. iOS中 项目开发易错知识点总结

    点击return取消textView 的响应者 - (BOOL)textFieldShouldReturn:(UITextField *)textField { [_contactTextFiled  ...

  3. iOS抓包Charles 操作

    今天就来看一下Mac上如何进行抓包,之前有一篇文章介绍了使用Fidder进行抓包 http://blog.csdn.net/jiangwei0910410003/article/details/198 ...

  4. 如何在VS2013中新建WindowsService定时任务

    http://jingyan.baidu.com/article/cd4c2979e9330d756f6e6070.html 很多人都想做定时任务,但是没有不知道如何下手,现在就用WindowsSer ...

  5. Android之Handler用法总结(1)

    方法一:(java习惯,在android平台开发时这样是不行的,因为它违背了单线程模型) 刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题 new Thread ...

  6. BLOB:大数据,大对象,在数据库中用来存储超长文本的数据,例如图片等

    将一张图片存储在mysql中,并读取出来(BLOB数据:插入BLOB类型的数据必须使用PreparedStatement,因为插入BLOB类型的数据无法使用字符串拼写): -------------- ...

  7. 周赛-kiki's game 分类: 比赛 2015-08-02 09:24 7人阅读 评论(0) 收藏

    kiki's game Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 40000/10000 K (Java/Others) Total S ...

  8. HTML学习之canves元素

    1:绘制画布 和在画布上绘制矩形 <!DOCTYPE html> <html> <head lang="en"> <meta charse ...

  9. Entity Framework 第五篇 状态跟踪

    本人建议尽量使用EntityState来表名Entry的状态,而不要使用Configuration.AutoDetectChangesEnabled自动状态跟踪,为什么我这么建议呢?他们到底有什么异同 ...

  10. DailyReport自动保存工具

    PS:自己初学C#,SharePoint时做的一个小tool. Friday, November 28, 2014 ​这个tool编译出来以后可以把部门的daily report保存到本地,数据库,和 ...