在本文中,我们将带你了解HDU-5845BestDivisiondp+字典树在这篇文章中,我们将为您详细介绍HDU-5845BestDivisiondp+字典树的方方面面,并解答字典树详解常见的疑惑,
在本文中,我们将带你了解HDU - 5845 Best Division dp + 字典树在这篇文章中,我们将为您详细介绍HDU - 5845 Best Division dp + 字典树的方方面面,并解答字典树详解常见的疑惑,同时我们还将给您一些技巧,以帮助您实现更有效的2016"百度之星" - 资格赛(Astar Round1)(hdu5685(线段树、乘法逆元),hdu5686(大数),hdu5687(字典树),hdu5688)、2019 年牛客多校第一场 I 题 Points Division 线段树 + DP、2019HDU 多校第三场 Distribution of books 二分 + DP、@hdu - 6584@ Meteor。
本文目录一览:- HDU - 5845 Best Division dp + 字典树(字典树详解)
- 2016"百度之星" - 资格赛(Astar Round1)(hdu5685(线段树、乘法逆元),hdu5686(大数),hdu5687(字典树),hdu5688)
- 2019 年牛客多校第一场 I 题 Points Division 线段树 + DP
- 2019HDU 多校第三场 Distribution of books 二分 + DP
- @hdu - 6584@ Meteor
HDU - 5845 Best Division dp + 字典树(字典树详解)
HDU - 5845
dp[ i ] 表示分完前 i 段, 最多能分几段。
我们能得到一个n2的dp, 然后用字典树优化掉。
我用了一个multiset去维护删除, 但实际上因为dp值有单调性, 所有维护sz就够了。
换成c++卡内从卡过去的。
//#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize(4) //#include<bits/stdc++.h> #include<cstdio> #include<algorithm> #include<set> #define LL long long #define LD long double #define ull unsigned long long #define fi first #define se second #define mk make_pair #define PLL pair<LL,LL> #define PLI pair<LL,int> #define PII pair<int,int> #define SZ(x) ((int)x.size()) #define ALL(x) (x).begin(),(x).end() #define fio ios::sync_with_stdio(false); cin.tie(0); using namespace std; const int N = 1e5 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 998244353; const double eps = 1e-8; const double PI = acos(-1); template<class T,class S> inline void add(T &a,S b) {a += b; if(a >= mod) a -= mod;} template<class T,class S> inline void sub(T &a,S b) {a -= b; if(a < 0) a += mod;} template<class T,class S> inline bool chkmax(T &a,S b) {return a < b ? a = b,true : false;} template<class T,class S> inline bool chkmin(T &a,S b) {return a > b ? a = b,true : false;} //mt19937 rng(chrono::steady_clock::Now().time_since_epoch().count()); const int LOG = 28; int n,up,len; int a[N]; int dp[N]; void read() { int p,q; scanf("%d%d%d",&a[1],&p,&q); for(int i = 2; i <= n; i++) { a[i] = (1LL * a[i - 1] * p + q) % 268435456; } for(int i = 1; i <= n; i++) { a[i] ^= a[i - 1]; } } int trietot,Rt; struct Trie { int ch[2],mx; } tr[N * LOG]; multiset<int> mulset[N * LOG]; inline int newNode() { trietot++; tr[trietot].ch[0] = 0; tr[trietot].ch[1] = 0; tr[trietot].mx = -1; mulset[trietot].clear(); return trietot; } void ins(int x,int val) { int u = Rt; for(int i = LOG - 1; i >= 0; i--) { chkmax(tr[u].mx,val); int v = tr[u].ch[x >> i & 1]; if(!v) tr[u].ch[x >> i & 1] = newNode(); v = tr[u].ch[x >> i & 1]; u = v; } mulset[u].insert(val); chkmax(tr[u].mx,val); } void del(int u,int x,int val,int d) { if(d == -1) { mulset[u].erase(mulset[u].lower_bound(val)); if(SZ(mulset[u])) tr[u].mx = *mulset[u].rbegin(); else tr[u].mx = -1; return; } del(tr[u].ch[x >> d & 1],x,val,d - 1); tr[u].mx = max(tr[tr[u].ch[0]].mx,tr[tr[u].ch[1]].mx); } int query(int x) { int ans = -1; bool limit = true; bool ban = false; int u = Rt; for(int i = LOG - 1; i >= 0; i--) { if(limit) { int sml = x >> i & 1; int big = sml ^ 1; int smlv = tr[u].ch[sml]; int bigv = tr[u].ch[big]; if(!(up >> i & 1)) { u = smlv; } else { if(tr[smlv].mx >= tr[bigv].mx) { limit = false; u = smlv; } else { chkmax(ans,tr[smlv].mx); u = bigv; } } } else { chkmax(ans,tr[u].mx); ban = true; break; } } if(!ban) chkmax(ans,tr[u].mx); return ans; } void init() { tr[0].mx = -1; trietot = 0; Rt = newNode(); } int main() { int T; scanf("%d",&T); while(T--) { init(); scanf("%d%d%d",&n,&up,&len); read(); ins(0,0); for(int i = 1,j = 0; i <= n; i++) { while(j + len < i) { if(dp[j] != -1) del(Rt,a[j],dp[j],LOG - 1); j++; } dp[i] = query(a[i]); if(dp[i] != -1) { dp[i]++; ins(a[i],dp[i]); } } if(dp[n] != -1) printf("%d\n",dp[n]); else puts("0"); } return 0; } /* */
2016"百度之星" - 资格赛(Astar Round1)(hdu5685(线段树、乘法逆元),hdu5686(大数),hdu5687(字典树),hdu5688)
Problem A
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5685
解题思路:
可以用线段树求解,但是数据有问题,前期一直re,不晓得哪里错了,看了讨论才知道,数据有问题,后期数据被更正过来。但是
在hdu交时,一直wrong,看别人比赛时的代码都是用乘法逆元做的,还以为自己的做法错了,后来好心人告知,hdu上的数据是比
赛时前期的数据,瞬间吐血。。。
AC代码(线段树):
#include <iostream> #include <cstdio> #include <cstring> #define lson id<<1 #define rson id<<1|1 using namespace std; const int MOD = 9973; const int N = 100005; char str[N]; int ans = 0; struct node{ int l,r,val; }tree[N<<2]; void build(int id,int l,int r){ tree[id].l = l; tree[id].r = r; if(l == r){ tree[id].val = str[l]-28; return ; } int mid = (l+r)>>1; build(lson,l,mid); build(rson,mid+1,r); tree[id].val = tree[lson].val*tree[rson].val%MOD; } int query(int id,int r){ if(tree[id].l >= l && tree[id].r <= r) return tree[id].val; int mid = (tree[id].l+tree[id].r)>>1; if(r <= mid) return query(lson,r); if(l > mid) return query(rson,r); return query(lson,mid)*query(rson,r)%MOD; } int main(){ int n; while(~scanf("%d",&n)){ scanf("%s",str+1); int a,b,len = strlen(str+1); build(1,1,len); while(n--){ scanf("%d%d",&a,&b); if(a < 1 || b > len){ printf("%d\n",ans); continue; }//如果以后数据被更正过来,可不要此步。 ans = query(1,a,b); printf("%d\n",ans); } } return 0; }
AC代码(乘法逆元):
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int MOD = 9973; const int N = 100005; char str[N]; int inv[MOD+5],no[N]; int main(){ inv[1] = 1; for(int i = 2; i < MOD; ++i) inv[i] = inv[MOD%i]*(MOD-MOD/i)%MOD; int n; while(~scanf("%d",str); int len = strlen(str); no[0] = 1; for(int i = 0; i < len; ++i) no[i+1] = no[i]*(str[i]-28)%MOD; while(n--){ int a,b; scanf("%d%d",&b); printf("%d\n",no[b]*inv[no[a-1]]%MOD); } } return 0; }
Problem B
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5686
解题思路:
这题hdu数据也有问题,拿比赛时的代码交上去Wrong了,后来才发现这题数据也有问题,n可以等于0.。。。而且等于0时,是输出
一空行。。。
AC代码:
import java.math.BigInteger; import java.util.Scanner; public class Main { public static void main(String[] args) { BigInteger[] dp = new BigInteger[205]; dp[1] = BigInteger.ONE; dp[2] = BigInteger.valueOf(2); for(int i = 3; i <= 200; ++i) dp[i] = dp[i-1].add(dp[i-2]); Scanner sca = new Scanner(system.in); while(sca.hasNext()){ int n = sca.nextInt(); if(n == 0){ System.out.println(); continue; } System.out.println(dp[n]); } } }
Problem C
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5687
解题思路:
这题题目不难,但是要注意一些细节,比如原来有hello,要你删除he后,剩下的前缀如果值为0了,也要被删除,这点注意好就行
了。(具体细节看deltrie这个函数)
AC代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; struct node{ int cnt; struct node *next[26]; node(){ cnt = 0; memset(next,sizeof(next)); } }; node *root = NULL; char op[10],str[35]; void build(char *s){ node *p = root; int l = strlen(s); for(int i = 0; i < l; i++){ if(p->next[s[i]-''a''] == NULL){ p->next[s[i]-''a''] = new node(); } p = p->next[s[i]-''a'']; p->cnt++; } } void del(node *root){ for(int i = 0; i < 26; ++i){ if(root->next[i] != NULL) delete(root->next[i]); } delete root; } void deltrie(char *s){ int l = strlen(s); node *p = root; for(int i = 0; i < l; ++i){ if(p->next[s[i]-''a''] == NULL) return ; p = p->next[s[i]-''a'']; } int num = p->cnt; node *q; p = root; for(int i = 0; i < l; ++i){ q = p->next[s[i]-''a'']; q->cnt -= num; if(q->cnt == 0){ del(q); p->next[s[i]-''a''] = NULL; return ; } p = p->next[s[i]-''a'']; } } bool findtrie(char *s){ node *p = root; int l = strlen(s); for(int i = 0; i < l; i++){ if(p->next[s[i]-''a''] == NULL) return false; p = p->next[s[i]-''a'']; } return true; } int main(){ int n; while(~scanf("%d",&n)){ root = new node; for(int i = 0; i < n; ++i){ scanf("%s%s",op,str); if(op[0] == ''i'') build(str); else if(op[0] == ''d'') deltrie(str); else{ if(findtrie(str)) puts("Yes"); else puts("No"); } } } return 0; }
Problem D
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5688
解题思路:
直接用map处理即可。。。
AC代码:
#include <iostream> #include <cstdio> #include <string> #include <map> #include <algorithm> using namespace std; map<string,int> ma; int main(){ int n; while(~scanf("%d",&n)){ ma.clear(); while(n--){ string name; cin>>name; sort(name.begin(),name.end()); printf("%d\n",ma[name]); ++ma[name]; } } return 0; }
2019 年牛客多校第一场 I 题 Points Division 线段树 + DP
题目链接
传送门
题意
给你 $n$ 个点,每个点的坐标为 $(x_i,y_i)$,有两个权值 $a_i,b_i$。 现在要你将它分成 $\mathbb {A},\mathbb {B}$ 两部分,使得在满足 “$\mathbb {A}$ 的点不能落在在 $\mathbb {B}$ 的点的右下方” 的条件下 $\sum\limits_{i\in\mathbb {A}} a_i+\sum\limits_{j\in\mathbb {B}} b_j$ 最大。
思路
这篇博客讲得很详细,大家可以看这位大佬的昂~
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://Code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 1e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int n;
vector<int> vec;
struct Point {
int x, y, a, b;
bool operator < (const Point& pp) const {
return x == pp.x ? y > pp.y : x < pp.x;
}
}point[maxn];
struct node {
int l, r;
LL mx, lazy;
}segtree[maxn<<2];
void push_up(int rt) {
segtree[rt].mx = max(segtree[lson].mx, segtree[rson].mx);
}
void push_down(int rt) {
LL x = segtree[rt].lazy;
segtree[rt].lazy = 0;
segtree[lson].lazy += x;
segtree[rson].lazy += x;
segtree[lson].mx += x;
segtree[rson].mx += x;
}
void build(int rt, int l, int r) {
segtree[rt].l = l, segtree[rt].r = r;
segtree[rt].mx = segtree[rt].lazy = 0;
if(l == r) return;
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
}
void update1(int rt, int pos, LL val) {
if(segtree[rt].l == segtree[rt].r) {
segtree[rt].mx = val;
return;
}
push_down(rt);
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(pos <= mid) update1(lson, pos, val);
else update1(rson, pos, val);
push_up(rt);
}
void update2(int rt, int l, int r, LL val) {
if(segtree[rt].l == l && segtree[rt].r == r) {
segtree[rt].mx += val;
segtree[rt].lazy += val;
return;
}
push_down(rt);
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(r <= mid) update2(lson, l, r, val);
else if(l > mid) update2(rson, l, r, val);
else {
update2(lson, l, mid, val);
update2(rson, mid + 1, r, val);
}
push_up(rt);
}
LL query(int rt, int l, int r) {
if(segtree[rt].l == l && segtree[rt].r == r) {
return segtree[rt].mx;
}
push_down(rt);
int mid = (segtree[rt].l + segtree[rt].r) >> 1;
if(r <= mid) return query(lson, l, r);
else if(l > mid) return query(rson, l, r);
else return max(query(lson, l, mid), query(rson, mid + 1, r));
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
while(~scanf("%d", &n)) {
vec.clear();
for(int i = 1; i <= n; ++i) {
scanf("%d%d%d%d", &point[i].x, &point[i].y, &point[i].a, &point[i].b);
vec.push_back(point[i].y);
}
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
sort(point + 1, point + n + 1);
for(int i = 1; i <= n; ++i) {
point[i].y = lower_bound(vec.begin(), vec.end(), point[i].y) - vec.begin() + 1;
}
int sz = vec.size();
build(1, 0, sz + 1);
for(int i = 1; i <= n; ++i) {
LL num = query(1, 0, point[i].y);
update1(1, point[i].y, num + point[i].b);
update2(1, 0, point[i].y - 1, point[i].a);
update2(1, point[i].y + 1, sz + 1, point[i].b);
}
printf("%lld\n", segtree[1].mx);
}
return 0;
}
2019HDU 多校第三场 Distribution of books 二分 + DP
题意:给你一个序列,你可以选择序列的一个前缀,把前缀分成 k 个连续的部分,要求这 k 个部分的区间和的最大值尽量的小,问这个最小的最大值是多少?
思路:首先看到最大值的最小值,容易想到二分。对于每个二分值 mid,我们判断原序列是否可以构成 k 个区间和小于等于 mid 的区间,这个可以用 DP 来做。我们先求出序列的前缀和,这样可以把区间和变成前缀相减的形式。之后,对于当前的前缀和 sum [i], 我们找前缀和大于等于 sum [i] - mid 的状态中 dp 值最大的向自己转移。如果最后存在状态的 dp 值大于等于 k,那么说明二分的 mid 值偏大,要缩小上边界。反之亦然。dp 用离散化 + 线段树优化,总复杂度 O (n * logn) * log (值域)
代码:
#include <bits/stdc++.h>
#define LL long long
#define ls (o << 1)
#define rs (o << 1 | 1)
using namespace std;
const int maxn = 200010;
LL sum[maxn];
LL a[maxn];
LL b[maxn];
int dp[maxn], mx[maxn * 4];
int n, m, tot;
void build(int o, int l, int r) {
if(l == r) {
mx[o] = -1;
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
mx[o] = max(mx[ls], mx[rs]);
}
void update(int o, int l, int r, int p, int val) {
if(l == r) {
mx[o] = max(mx[o], val);
return;
}
int mid = (l + r) >> 1;
if(p <= mid) update(ls, l, mid, p, val);
else update(rs, mid + 1, r, p, val);
mx[o] = max(mx[ls], mx[rs]);
}
int query(int o, int l, int r, int ql, int qr) {
if(ql > qr) return -1;
if(l >= ql && r <= qr) {
return mx[o];
}
int mid = (l + r) >> 1, ans = -1;
if(ql <= mid) ans = max(ans, query(ls, l, mid, ql, qr));
if(qr > mid) ans = max(ans, query(rs, mid + 1, r, ql, qr));
return ans;
}
bool solve(LL mid) {
build(1, 1, tot);
update(1, 1, tot, lower_bound(b + 1, b + 1 + tot, 0) - b, 0);
for (int i = 1; i <= n; i++) {
int p = lower_bound(b + 1, b + 1 + tot, sum[i] - mid) - b;
int p1 = lower_bound(b + 1, b + 1 + tot, sum[i]) - b;
int tmp = query(1, 1, tot, p, tot);
if(tmp == -1) dp[i] = -1;
else dp[i] = tmp + 1;
if(dp[i] >= m) return 0;
update(1, 1, tot, p1, dp[i]);
}
return 1;
}
int main() {
int T;
// freopen("input.txt", "r", stdin);
// freopen("1.in", "r", stdin);
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
sum[i] = sum[i - 1] + a[i];
b[i] = sum[i];
}
b[n + 1] = 0;
sort(b + 1, b + 1 + n + 1);
tot = unique(b + 1, b + 1 + n + 1) - (b + 1);
LL l = -2e14, r = 2e14;
while(l < r) {
LL mid = (l + r) >> 1;
if(solve(mid)) l = mid + 1;
else r = mid;
}
printf("%lld\n", l);
}
}
@hdu - 6584@ Meteor
[toc]
@description@
询问第 k 小的分子分母 ≤ n 的既约分数。
Input 第一行包含一个整数 T(T≤10^2),表示数据组数。 接下来 T 行每行两个整数 n, k,表示一组询问。保证询问的答案在 (0,1] 范围内。
Output 输出 T 行,每行包含一个分数 p/q,要求 gcd(p, q) = 1。
Sample Input 5 4 6 5 1 9 9 3 4 7 11 Sample Output 1/1 1/5 1/3 1/1 3/5
Hint 杭电支持 __int128。
@solution@
寻找第 k 小,不难想到使用二分法。 则假如二分到一个值 x,≤ x 的既约分数可以用如下式子计算: $$ans = \sum_{i=1}^{n}\sum_{j=1}^{\lfloor x*i\rfloor}[gcd(i, j) = 1]$$
对其进行反演可得: $$ans = \sum_{i=1}^{n}\sum_{d=1}^{d|i}\mu(d)\lfloor\frac{xi}{d}\rfloor \ = \sum_{ab\le n}\mu(a)\lfloor xb \rfloor \ = \sum_{i=1}^{n}\mu(i)\sum_{j=1}^{\lfloor\frac{n}{i}\rfloor}\lfloor x*j\rfloor$$
后面那一项如果以 j 为横坐标,x 为常数,可以看成直线 y = x*j 下的整点数量,联想到类欧几里得算法。 但是 x 是个实数的话类欧几里得是不可行的。不过假如我们二分直接使用分数进行二分的话,就的确可以使用类欧几里得算法。 加上预处理莫比乌斯函数前缀和 + 整除分块的话,可以在 $O(\sqrt{n}*\log n)$ 的时间内完成一次计算。
实数二分需要一个迭代次数,那么我们多少次才能保证精度呢? 假如 a/b 与 c/d 都是两个分子分母 ≤ n 的不相等的分数,则 a/b - c/d = (ad - bc)/(b*d) 最小时,分子 ad - bc = 1,分母 b*d = n*n。 所以精度的一个上限是 1/(n*n),于是取 log(n*n) 次二分就可以保证精度。这样最大次数为 40。
下一个问题是,我们如何找到 > p/q 且最小的既约分数呢? 我们可以在 SB tree(Stern-Brocot Tree)上进行寻找。这棵树在具体数学上有介绍它的性质,WC2019 上也有讲一点。 这里有一篇简略的博客。
我们这道题只需要知道 SB Tree 的生成方式,以及它的几个性质: (1)SB Tree 是一个既约分数的二叉搜索树。 (2)SB Tree 父亲的分母小于儿子的分母。 而这些足以让我们实现我们的目的。寻找答案的这个过程是 O(n)。
@accepted code@
#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cmath>
typedef long long ll;
const int MAXN = 1000000;
__int128 gcd(__int128 a, __int128 b) {return (b == 0) ? a : gcd(b, a % b);}
struct frac{
__int128 a, b;
frac(__int128 _a=0, __int128 _b=0):a(_a), b(_b) {}
friend bool operator < (frac a, frac b) {return a.a*b.b < b.a*a.b;}
friend frac operator + (frac a, frac b) {
__int128 d = gcd(a.b*b.a + a.a*b.b, a.b*b.b);
return frac((a.b*b.a + a.a*b.b)/d, a.b*b.b/d);
}
friend frac operator / (frac a, __int128 k) {return frac(a.a, k*a.b);}
};
int n; ll k;
bool search_answer(frac p) {
frac ans = frac(1, 1), a = frac(0, 1), b = frac(1, 1);
while( a.b + b.b <= n ) {
frac c = frac(a.a + b.a, a.b + b.b);
if( p < c )
ans = b = c;
else a = c;
}
printf("%d/%d\n", int(ans.a), int(ans.b));
}
ll smiu[MAXN + 5];
__int128 func(__int128 A, __int128 B, __int128 C, __int128 n) {
if( A/C ) return func(A % C, B, C, n) + A/C*(n + 1)*n/2;
if( B/C ) return func(A, B % C, C, n) + B/C*(n + 1);
if( A == 0 ) return 0;
__int128 m = (A*n + B) / C;
if( m == 0 ) return 0;
return m*n - func(C, C - 1 - B, A, m - 1);
}
ll check(frac x) {
ll ans = 0; int p = 1, q = 1;
// printf("? %d %d\n", int(x.a), int(x.b));
while( p <= n ) {
q = p, p = (n/(n/q));
ll s = smiu[p] - smiu[q - 1];
ans += s*func(x.a, 0, x.b, n/p);
// printf("! %lld\n", ans);
p++;
}
return ans;
}
void solve() {
scanf("%d%lld", &n, &k);
int lim = log2(1LL*n*n) + 1;
frac le = frac(0, 1), ri = frac(1, 1);
for(int i=0;i<=lim;i++) {
frac mid = (le + ri) / 2;
if( check(mid) < k )
le = mid;
else ri = mid;
}
search_answer(le);
}
bool nprm[MAXN + 5];
void init() {
for(int i=1;i<=MAXN;i++)
smiu[i] = 1;
for(int i=2;i<=MAXN;i++) {
if( !nprm[i] ) {
for(int j=i;j<=MAXN;j+=i) {
nprm[j] = true;
if( (j/i) % i == 0 )
smiu[j] = 0;
else smiu[j] *= -1;
}
}
}
for(int i=1;i<=MAXN;i++)
smiu[i] += smiu[i-1];
}
int main() {
init();
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
算是还不错的一道题,不过涉及到的知识点比较冷门。。。 我才不会说理解类欧几里得算法花了我一个晚上的时间。
找答案的时候还不能在树上递归找,不然会爆栈。正确的操作是迭代找答案。
今天关于HDU - 5845 Best Division dp + 字典树和字典树详解的讲解已经结束,谢谢您的阅读,如果想了解更多关于2016"百度之星" - 资格赛(Astar Round1)(hdu5685(线段树、乘法逆元),hdu5686(大数),hdu5687(字典树),hdu5688)、2019 年牛客多校第一场 I 题 Points Division 线段树 + DP、2019HDU 多校第三场 Distribution of books 二分 + DP、@hdu - 6584@ Meteor的相关知识,请在本站搜索。
本文标签: