http://219.244.176.199/JudgeOnline/problem.php?id=1239

这是这次陕西省赛的G题,题目大意是一个n*n的点阵,点坐标从(1, 1)到(n, n),每个点都有权值,然后从(x, y)引x轴的垂线,然后构成一个三角形,三个顶点分别是(0, 0),(x, 0)和(x, y)。求三角形内点的权值和,包括边界,n的范围是1000,m的范围是100000,说起来也比较坑。。学弟n*m的复杂度竟然水过去了,目测比赛数据比较水。。不过我挂到我们OJ上给了一组随机数据,一组极限数据,然后学弟就T掉了。。(坑学弟系列。。)

不水了,这个题目感觉本身是个很好的题。

刚拿到题,我的第一反应肯定是求出所有的p(x, y),然后往dp的转移方程去考虑。但是发现单纯的递推感觉问题更复杂了。。

但是发现一个重要点就是,斜率小的点,必定被它后面斜率大的点包括。然后,我就想到按x轴枚举每一列,维护每个斜率出现的权值和。那么每一个大的斜率必然是前面枚举过的小的斜率的和,于是便可以树状数组维护了。关键是解决斜率的离散化,我的第一想法是map来进行Hash。当时有了这个思路,本来想着应该可以抢个一血什么的。。结果打到一个小时左右的时候,发现这题已经被好几个人A掉了。。。(什么鬼。。)。我的第一发T了。。通过本地测试,map那个Hash实在太慢了。。于是我开始考虑怎么优化这个Hash,想了好几个办法,要满足y1/x1 < y2/x2,并且Hash(x1, y1) < Hash(x2, y2)的函数实在是没办法。。中间还交了两次错误的Hash方法。。最后队友提醒下,发现那个离散化的过程完全可以一开始预处理,不需要每组数据在线完成,因为数据只有1000*1000,那么所有斜率必然在这个范围内。然后终于A掉了。。

预处理复杂度:O(n*n*log(n*n))

打表:O(n*n*log(n*n))

在线查询:O(m)

最后总的复杂度是O(n*n*log(n*n)+T*(m+n*n*log(n*n)))

如果m大一点的话,这个复杂度还是比较优秀的。。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <string>
#define LL long long using namespace std; const int maxM = ;
const int maxN = ;
LL d[maxN]; int lowbit(int x)
{
return x&(-x);
} void add(int id, int pls)
{
while(id <= maxN)//id最大是maxN
{
d[id] += pls;
id += lowbit(id);
}
} LL sum(int to)
{
LL s = ;
while(to > )
{
s = s + d[to];
to -= lowbit(to);
}
return s;
} int gcd(int a, int b)
{
int r;
while (b != )
{
r = b;
b = a%b;
a = r;
}
return a;
} struct node
{
int x, y; void create(int xx, int yy)
{
int t = gcd(xx, yy);
x = xx/t;
y = yy/t;
} bool operator<(node k) const
{
return k.x*y < k.y*x;
}
}; int n, a[maxM][maxM];
LL p[maxM][maxM];
map<node, int> Hash; void init()
{
node t;
for (int i = ; i < maxM; ++i)
for (int j = ; j < maxM; ++j)
{
t.create(i, j);
Hash[t] = ;
}
map<node, int>::iterator it;
int cnt = ;
for (it = Hash.begin(); it != Hash.end(); ++it)
it->second = cnt++;
} void input()
{
memset(d, , sizeof(d));
node t;
int to;
scanf("%d", &n);
for (int i = n; i >= ; --i)
for (int j = ; j <= n; ++j)
scanf("%d", &a[i][j]);
for (int j = ; j <= n; ++j)
{
for (int i = ; i <= n; ++i)
{
t.create(j, i);
to = Hash[t];
add(to, a[i][j]);
p[j][i] = sum(to);
}
}
} void work()
{
int m, u, v;
scanf("%d", &m);
for (int i = ; i <= m; ++i)
{
scanf("%d%d", &u, &v);
printf("%lld\n", p[u][v]);
}
} int main()
{
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
init();
int T;
scanf("%d", &T);
for (int times = ; times <= T; ++times)
{
printf("Case #%d:\n", times);
input();
work();
}
return ;
}

更新一点东西。

虽然还没想到很好的Hash方式。不过队友给了一个很好的Hash生成方式。不管是用map,还是排序,预处理Hash的复杂度是n*n*long(n*n)的。

但是考虑到每一行的点斜率是单调的,于是类似于多路归并。我先选取每一行斜率最小的点,也就是每一行最后一个点,一共n个点。然后进行n路归并,用优先队列,也就是堆维护这n个斜率。然后每次选取队列首的斜率,那么这个斜率必然是当前最小的。然后赋值Hash值,后将刚斜率所在行的前一个点加入优先队列,如此循环,实现了n路归并,也就是一个排序离散化的过程。这样就可以在复杂度n*n*logn的时间内实现Hash了。复杂度降低了一个常数级。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <vector>
#include <string>
#define LL long long using namespace std; const int maxM = ;
const int maxN = ;
LL d[maxN]; int lowbit(int x)
{
return x&(-x);
} void add(int id, int pls)
{
while(id <= maxN)//id×î´óÊÇmaxN
{
d[id] += pls;
id += lowbit(id);
}
} LL sum(int to)
{
LL s = ;
while(to > )
{
s = s + d[to];
to -= lowbit(to);
}
return s;
} int gcd(int a, int b)
{
int r;
while (b != )
{
r = b;
b = a%b;
a = r;
}
return a;
} struct node
{
int x, y; void create(int xx, int yy)
{
int t = gcd(xx, yy);
x = xx/t;
y = yy/t;
} bool operator<(node k) const
{
return k.x*y > k.y*x;
}
}; int n, a[maxM][maxM];
LL p[maxM][maxM];
int Hash[maxM][maxM];
bool vis[maxM][maxM]; void init()
{
memset(vis, false, sizeof(vis));
memset(Hash, , sizeof(Hash));
node t, k;
priority_queue<node> q;
int cnt = ;
for (int i = ; i < maxM; ++i)
{
t.x = maxM-;
t.y = i;
q.push(t);
vis[t.x][t.y] = true;
}
while (!q.empty())
{
t = q.top();
q.pop();
vis[t.x][t.y] = false;
k.create(t.x, t.y);
if (!Hash[k.x][k.y]) Hash[k.x][k.y] = cnt++;
if (t.x > && !vis[t.x-][t.y])
{
t.x--;
q.push(t);
vis[t.x][t.y] = true;
}
}
} void input()
{
memset(d, , sizeof(d));
node t;
int to;
scanf("%d", &n);
for (int i = n; i >= ; --i)
for (int j = ; j <= n; ++j)
scanf("%d", &a[i][j]);
for (int j = ; j <= n; ++j)
{
for (int i = ; i <= n; ++i)
{
t.create(j, i);
to = Hash[t.x][t.y];
add(to, a[i][j]);
p[j][i] = sum(to);
}
}
} void work()
{
int m, u, v;
scanf("%d", &m);
for (int i = ; i <= m; ++i)
{
scanf("%d%d", &u, &v);
printf("%lld\n", p[u][v]);
}
} int main()
{
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
init();
int T;
scanf("%d", &T);
for (int times = ; times <= T; ++times)
{
printf("Case #%d:\n", times);
input();
work();
}
return ;
}

ACM学习历程—SNNUOJ 1239 Counting Star Time(树状数组 && 动态规划 && 数论)的更多相关文章

  1. HDU 5862 Counting Intersections(离散化+树状数组)

    HDU 5862 Counting Intersections(离散化+树状数组) 题目链接http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 D ...

  2. HDU 5862 Counting Intersections (树状数组)

    Counting Intersections 题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 Description Given ...

  3. 13年山东省赛 Boring Counting(离线树状数组or主席树+二分or划分树+二分)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud 2224: Boring Counting Time Limit: 3 Sec   ...

  4. HDU 5862 Counting Intersections 扫描线+树状数组

    题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=5862 Counting Intersections Time Limit: 12000/ ...

  5. TOJ 4105 Lines Counting(离线树状数组)

    4105.   Lines Counting Time Limit: 2.0 Seconds   Memory Limit: 150000K Total Runs: 152   Accepted Ru ...

  6. ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)

    Description Byteland国家的网络单向传输系统可以被看成是以首都 Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的 ...

  7. ACM学习历程—SNNUOJ 1116 A Simple Problem(递推 && 逆元 && 组合数学 && 快速幂)(2015陕西省大学生程序设计竞赛K题)

    Description Assuming a finite – radius “ball” which is on an N dimension is cut with a “knife” of N- ...

  8. ACM学习历程—ZOJ3777 Problem Arrangement(递推 && 状压)

    Description The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem sett ...

  9. ACM学习历程—HDU 4287 Intelligent IME(字典树 || map)

    Description We all use cell phone today. And we must be familiar with the intelligent English input ...

随机推荐

  1. 强大的jQuery幻灯片播放插件 支持全拼、拖拽和下载等功能

    在线演示 本地下载

  2. jQuery幻灯片播放器插件

    在线演示 本地下载

  3. 会话控制Session的应用

    Session技术与Cookie相似,都是用来存储使用者的相关资料.但是最大不同之处在于Cookie是将数据存放于客户端计算机中,而Session则是将数据存放于服务器系统下. 在Web技术发展史上, ...

  4. VRChat简易教程2-创建一个最基本的世界(world)

    一.准备工作 1 先确保你安装了unity并导入了sdk 教程:https://www.cnblogs.com/cation/p/10311702.html 2 按之前的教程新建一个project并导 ...

  5. Java Lambda表达

    Java 8 lambda表达式示例 我个人对Java 8发布非常激动,尤其是lambda表达式和流API.越来越多的了解它们,我能写出更干净的代码.虽然一开始并不是这样.第一次看到用lambda表达 ...

  6. AMD C1E SUPPORT

    •C1E是一种电源管理状态,它可以让处理器节能不限于处理器内核.在CIE状态,可以通过降低内存时钟速度.关闭HT技术,来降低处理器能耗.这种新功能对于12核的处理器极其重要,因为这种处理器在设计上既增 ...

  7. UVA 10288 Coupons (概率)

    题意:有n种纸片无限张,随机抽取,问平均情况下抽多少张可以保证抽中所有类型的纸片 题解:假设自己手上有k张,抽中已经抽过的概率为 s=k/n:那抽中下一张没被抽过的纸片概率为 (再抽一张中,两张中,三 ...

  8. Rotate List ,反转链表的右k个元素

    问题描述: Given a list, rotate the list to the right by k places, where k is non-negative. For example:G ...

  9. Divide two numbers,两数相除求商,不能用乘法,除法,取模运算

    问题描述:求商,不能用乘法,除法,取模运算. 算法思路:不能用除法,那只能用减法,但是用减法,超时.可以用位移运算,每次除数左移,相当于2倍. public class DividTwoInteger ...

  10. xpath选择器简介及如何使用

    xpath选择器简介及如何使用 一.总结 一句话总结:XPath 的全称是 XML Path Language,即 XML 路径语言,它是一种在结构化文档(比如 XML 和 HTML 文档)中定位信息 ...