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. Linux 下多核CPU知识【转】

    转自:http://www.cnblogs.com/dongzhiquan/archive/2012/02/16/2354977.html 1. 在Linux下,如何确认是多核或多CPU: #cat ...

  2. CentOS 7安装Gnome GUI 图形界面

    当你安装centos服务器版本的时候,系统默认是不会安装 CentOS 的图形界面程序的,比如:gnome或者kde, 那么如果你想在图形界面下工作的话,可以手动来安装CentOS Gnome GUI ...

  3. 9、Http回顾/Servlet

    1 Http回顾 Http协议: 1)http协议: 对浏览器客户端和服务器端之间数据传输的格式规范. 2)http请求:浏览器->服务器端 格式: 请求行(请求方式(GET/POST) 请求资 ...

  4. C#中一个关于不同窗体间的颜色参数的传递

    1目标是 在弹出菜单中选择颜色,在主菜单中对控件进行操作(弹出菜单选择的颜色就是主菜单控件的颜色) 2颜色属性需要来回转换(也许不用转换,暂时还不会,有会的提醒下,TKS) 3用到一个颜色控件(col ...

  5. MySQL连接字符串总结

    一.MySQL Connector/ODBC 2.50 (MyODBC 2.50)连接方式 1.本地数据库连接 Driver={MySQL};Server=localhost;Option=16834 ...

  6. JDBC批量Insert深度优化(有事务)

    环境: MySQL 5.1 RedHat Linux AS 5 JavaSE 1.5 DbConnectionBroker 微型数据库连接池   测试的方案: 执行10万次Insert语句,使用不同方 ...

  7. Crashing Robots 分类: POJ 2015-06-29 11:44 10人阅读 评论(0) 收藏

    Crashing Robots Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8340   Accepted: 3607 D ...

  8. ubuntu查看内存占用和查看cpu使用情况的简单方法(ubuntu内存管理)

    单独查看内存使用情况的命令:free -m查看内存及cpu使用情况的命令:top也可以安装htop工具,这样更直观,安装命令如下:sudo apt-get install htop安装完后,直接输入命 ...

  9. 使用jquery构建Metro style 返回顶部

    个人一直对metro风格的东西情有独钟,偶然间在腾讯网看到一款小插件,蓝色Metro风格的,所以决定把它放到我的博客中,这样做应该不会有版权问题吧orz.. Complete code 后言 我把他原 ...

  10. FZU 2168 防守阵地 I

    Problem Description 部队中共有N个士兵,每个士兵有各自的能力指数Xi,在一次演练中,指挥部确定了M个需要防守的地点,按重要程度从低到高排序,依次以数字1到M标注每个地点的重要程度, ...