ICPC World Finals 2019 题解
【A】Azulejos
题意简述:
有两排瓷砖,每排都有 \(n\) 个,每个瓷砖有高度 \(h_i\) 和价格 \(p_i\) 两种属性。
你需要分别重新排列这两排瓷砖,使得同一排的瓷砖满足价格不降,后一排的瓷砖的高度严格大于前一排对应瓷砖的高度。
判断无解或输出一种合法方案。
题解:
首先要满足价格不降,那么先把两排瓷砖分别按照 \(p_i\) 排序。
如果同一排中的两个瓷砖 \(p_i\) 不同,那么顺序已经确定。但是如果 \(p_i\) 相同,就可以任意交换顺序。
也就是说,前后两排中的瓷砖都被划分为了若干区间,每个区间中的瓷砖的 \(p_i\) 都相同,而且可以任意交换顺序。
让我们从两排的第一个区间开始考虑,这时有两种情况。
第一种是前一排的区间长度较短,那么此时前排的区间中的每个瓷砖都需要与后排的区间中的某个瓷砖配对。
那么在保证尽量能构造出解的前提下,最好把更多更优(\(h_i\) 尽量大)的后排的瓷砖留给后续考虑。
这引出一个贪心策略,使用 set
按照 \(h_i\) 为关键字维护瓷砖,
对于前排的每个瓷砖,在后排中寻找一个 \(h_i\) 尽量小,但是比当前瓷砖大的瓷砖与其配对,这样可以保证留给后面的瓷砖尽量优。
第二种是后一排的区间长度较短,相反地,我们对于后排的每个瓷砖寻找前排中 \(h_i\) 尽量大,但是比当前瓷砖小的瓷砖与其配对即可。
这种策略可以保证留给后续考虑的瓷砖尽量优。那么我们只需要对还未配对的瓷砖继续考虑即可。
不难写出代码,时间复杂度为 \(\mathcal{O}(n\log n)\):
#include <cstdio>
#include <algorithm>
#include <set>
const int MN = 500005;
int N, Ans1[MN], Ans2[MN];
struct dat{ int p, h, id; dat() {} dat(int h, int id) : h(h), id(id) {} } a1[MN], a2[MN];
inline bool operator <(dat i, dat j) { return i.h == j.h ? i.id < j.id : i.h < j.h; }
std::set<dat> s1, s2;
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i) scanf("%d", &a1[i].p);
for (int i = 1; i <= N; ++i) scanf("%d", &a1[i].h);
for (int i = 1; i <= N; ++i) scanf("%d", &a2[i].p);
for (int i = 1; i <= N; ++i) scanf("%d", &a2[i].h);
for (int i = 1; i <= N; ++i) a1[i].id = a2[i].id = i;
std::sort(a1 + 1, a1 + N + 1, [](dat i, dat j) { return i.p < j.p; });
std::sort(a2 + 1, a2 + N + 1, [](dat i, dat j) { return i.p < j.p; });
int cnt = 0;
for (int i = 0; i <= N; ++i) {
if (a1[i].p != a1[i + 1].p || a2[i].p != a2[i + 1].p) {
if (s1.size() < s2.size()) {
for (auto j : s1) {
auto it = s2.lower_bound(dat(j.h, 1));
if (it != s2.begin()) {
--it, ++cnt;
Ans1[cnt] = j.id;
Ans2[cnt] = it->id;
s2.erase(it);
}
else return puts("impossible"), 0;
}
s1.clear();
}
else {
for (auto j : s2) {
auto it = s1.upper_bound(dat(j.h, N));
if (it != s1.end()) {
++cnt;
Ans2[cnt] = j.id;
Ans1[cnt] = it->id;
s1.erase(it);
}
else return puts("impossible"), 0;
}
s2.clear();
}
if (a1[i].p != a1[i + 1].p)
for (int j = i + 1; j <= N && a1[j].p == a1[i + 1].p; ++j)
s1.insert(a1[j]);
if (a2[i].p != a2[i + 1].p)
for (int j = i + 1; j <= N && a2[j].p == a2[i + 1].p; ++j)
s2.insert(a2[j]);
}
}
for (int i = 1; i <= N; ++i) printf("%d ", Ans1[i]); puts("");
for (int i = 1; i <= N; ++i) printf("%d ", Ans2[i]); puts("");
return 0;
}
【B】Beautiful Bridges
题意简述:
给定一个地形剖面图,用 \(n\)(\(n\le 10^4\))个点描述,点 \(i\) 和点 \(i+1\) 之间有直线连接的地面。
你需要建一座拱桥,连接点 \(1\) 和点 \(n\),桥面的高度为 \(h\)。
你可以在桥中间建若干个柱子,以分配重量,柱子只能恰好建在给出的 \(n\) 个点上(点 \(1\) 和点 \(n\) 上必须有柱子)。
相邻的两根柱子之间需要建一个半圆形的拱,准确地说,拱的半径为两根柱子之间的距离的一半,并且与两根柱子和桥面相切。
拱可以与地面相切,但不能相交。
同时,桥的花费与柱子高度和拱面积有关,具体地,给出两个参数 \(\alpha\) 和 \(\beta\),
则花费为 \(\alpha\sum_{i=1}^{k}h_i+\beta\sum_{i=1}^{k-1}d_i^2\),其中 \(k\) 为柱子数量,\(h_i\) 为第 \(i\) 个柱子的高度,\(d_i\) 为第 \(i\) 个柱子到第 \(i+1\) 个柱子的距离。
问是否可以建出桥,若可以,问最小花费。
题解:
因为 \(n\) 不大,我们猜测正解是 \(\mathcal{O}(n^2)\) 的。
考虑一个简单的 DP:\(\mathrm{f}[i]\) 表示从 \(1\) 号点连接到 \(i\) 号点的最小花费,若不可行则为 \(\infty\)。
那么 \(\mathrm{f}[i]\) 可以从任意一个合法的 \(\mathrm{f}[j]\)(\(1\le j<i\))转移而来,多出的花费为 \(\alpha(h-y_i)+\beta(x_i-x_j)^2\)。
这样总转移数就是 \(\mathcal{O}(n^2)\) 的,但是要判断一个转移是否合法还需要 \(\mathcal{O}(i-j)\) 的时间,总复杂度提高到 \(\mathcal{O}(n^3)\)。
这时有两种优化思路,从转移数或判断合法性入手均可,但是观察发现合法转移点无明显规律,所以考虑如何优化判断合法性。
一个 \(j\) 到 \(i\) 的转移是否合法,取决于 \(j\) 到 \(i\) 之间的每个点是否和拱形相交。
注意到 \(i\) 固定,考虑利用这个性质,对每个 \(j\) 判断转移是否合法。
可以发现,对于每个 \(j\) 点,拱形的右端点固定时,此点不会与拱形相交的左端点形成一个区间,而区间本身可以通过解方程得到。
从右往左考虑每个 \(j\) 点,将它们对应的区间并起来便可以得到合法区间,对与每个转移只要判断 \(x_j\) 是否在合法区间内部即可。
据此写出代码,复杂度 \(\mathcal{O}(n^2)\)。
#include <cstdio>
#include <cmath>
#include <algorithm>
typedef long long LL;
const LL Inf = 0x3f3f3f3f3f3f3f3f;
const int MN = 10005;
inline LL MySqrt(LL x) {
LL y = sqrt(x);
while (y * y > x) --y;
while ((y + 1) * (y + 1) <= x) ++y;
return y;
}
int N;
LL H, Alpha, Beta;
LL px[MN], py[MN];
LL f[MN];
int main() {
scanf("%d%lld%lld%lld", &N, &H, &Alpha, &Beta);
for (int i = 1; i <= N; ++i) scanf("%lld%lld", &px[i], &py[i]);
f[1] = Alpha * (H - py[1]);
for (int i = 2; i <= N; ++i) {
f[i] = Inf;
LL Lb = px[i] - 2 * (H - py[i]), Rb = px[i];
for (int j = i - 1; j >= 1; --j) {
LL C1 = px[i] - px[j], C2 = H - py[j];
LL Sqrt = MySqrt(8 * C1 * C2);
LL MIN = px[i] - 2 * (C1 + C2) - Sqrt;
LL MAX = px[i] - 2 * (C1 + C2) + Sqrt;
if (px[i] - px[j] <= 2 * (H - py[j])) MAX = px[j];
Lb = std::max(Lb, MIN);
Rb = std::min(Rb, MAX);
if (Lb <= px[j] && px[j] <= Rb)
f[i] = std::min(f[i], f[j] + Alpha * (H - py[i]) + Beta * (px[i] - px[j]) * (px[i] - px[j]));
}
}
if (f[N] != Inf) printf("%lld\n", f[N]);
else puts("impossible");
return 0;
}
【C】Checks Post Facto
待补
【D】Circular DNA
题意简述:
一个长度为 \(n\) 的环形 DNA 序列,以顺时针顺序给出,其中每个基因有类型和编号两个属性,类型是 s
(头)或 e
(尾)中的一种,而编号是 \(1\) 到 \(10^6\) 中的整数。
你需要在某个地方切断,按照顺时针顺序拉成链后,最大化能够完美匹配的基因编号个数。
一个基因编号 \(i\) 是能够完美匹配的,当且仅当它在链中对应的所有基因,将 s
看作左括号,e
看作右括号,可以匹配成非空的合法括号序列。
如果有多个位置满足最大化的条件,输出最小的位置。
题解:
考虑每个基因编号,首先左括号个数要等于右括号个数,然后把左括号看作 \(+1\),右括号看作 \(-1\),在环上走一遍形成一个折线图,只有从其最低点开始,才能形成合法括号序。
那么把所有最低点(可能有多个)左右两边的 e
和 s
之间的位置都是可行的。
也就是区间加 \(1\),最后查询全局,差分-前缀和即可。
不难写出代码,复杂度 \(\mathcal{O}(n+m)\),\(m\) 为值域范围:
#include <cstdio>
#include <vector>
inline void getStr(int &Typ, int &Idt) {
char ch; Idt = 0;
while ((ch = getchar()) != 'e' && ch != 's') ;
Typ = ch == 's' ? 1 : -1, ch = getchar();
while (Idt = Idt * 10 + (ch ^ '0'), (ch = getchar()) >= '0' && ch <= '9') ;
}
const int MN = 1000005;
const int M = 1000000;
int N;
int Ty[MN], Id[MN], S[MN], Ans[MN];
std::vector<int> G[MN];
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i)
getStr(Ty[i], Id[i]),
G[Id[i]].push_back(i);
for (int id = 1; id <= M; ++id) {
int Sum = 0, Mn = 0;
for (auto i : G[id]) {
Sum += Ty[i];
S[i] = Sum;
if (Mn > Sum) Mn = Sum;
}
if (Sum) continue;
for (int i = 0; i < (int)G[id].size(); ++i) {
if (S[G[id][i]] == Mn) {
if (i < (int)G[id].size() - 1)
++Ans[G[id][i] + 1], --Ans[G[id][i + 1] + 1];
else {
++Ans[G[id][i] % N + 1], --Ans[G[id][0] + 1];
if (G[id][i] != N) ++Ans[1];
}
}
}
}
for (int i = 1; i <= N; ++i) Ans[i] += Ans[i - 1];
int Ai = 1, Av = Ans[1];
for (int i = 2; i <= N; ++i)
if (Ans[i] > Av) Ai = i, Av = Ans[i];
printf("%d %d\n", Ai, Av);
return 0;
}
【E】Dead-End Detector
题意简述:
有一张 \(n\) 个点 \(m\) 条边的简单无向图。
如果走过一条边 \(u\to v\) 后,不掉头无法返回到 \(u\),这条边就是对 \(u\) 来说的“死路”。
你需要对每个死路标记路标,但是有的路标是多余的。
如果从一个死路 \(u\to v\) 开始可以不掉头地走到另一个死路 \(u'\to v'\),那么后者 \(u'\to v'\) 就是多余的。
最后问要标记多少路标,输出每对 \(u\to v\),按照 \(u\) 为第一关键字,\(v\) 为第二关键字排序。
题解:
对每个连通块分别考虑。
如果当前连通块无环,也就是一棵树,那么所有方向的边都是死路,但是为了防止多余,应该在所有叶子(度数为 \(1\) 的点)处放置路标。
如果当前连通块有环(这里的环指的是不掉头,并且首尾相接的路径),那么环内的边都不是死路,指向环的边也不是死路,只有远离环的边才是死路,为了防止多余,应该在所有离开环的边处放置路标。
维护一个队列,每次删除度数为 \(1\) 的点,不难区分这些情况。
不难写出代码,复杂度 \(\mathcal{O}(n+m)\):
#include <cstdio>
#include <algorithm>
#include <vector>
const int MN = 500005;
int N, M;
std::vector<int> G[MN];
int d[MN];
int vis[MN], que[MN], l, r;
int Ans, A1[MN], A2[MN];
int main() {
scanf("%d%d", &N, &M);
for (int i = 1; i <= M; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
++d[u], ++d[v];
}
for (int i = 1; i <= N; ++i)
std::sort(G[i].begin(), G[i].end());
l = 1, r = 0;
for (int i = 1; i <= N; ++i)
if (d[i] == 1) vis[i] = 1, que[++r] = i;
while (l <= r) {
int u = que[l++];
for (auto v : G[u]) {
if (!vis[v] && --d[v] == 1)
vis[v] = 1, que[++r] = v;
}
}
l = 1, r = 0;
for (int i = 1; i <= N; ++i) vis[i] = 0;
for (int i = 1; i <= N; ++i)
if (d[i] > 1) vis[i] = 1, que[++r] = i;
while (l <= r) {
int u = que[l++];
for (auto v : G[u]) {
if (!vis[v])
vis[v] = 1, que[++r] = v;
}
}
for (int u = 1; u <= N; ++u) {
for (auto v : G[u]) {
if (!vis[u] && G[u].size() == 1)
A1[++Ans] = u, A2[Ans] = v;
if (vis[u] && d[u] > 1 && d[v] == 1)
A1[++Ans] = u, A2[Ans] = v;
}
}
printf("%d\n", Ans);
for (int i = 1; i <= Ans; ++i)
printf("%d %d\n", A1[i], A2[i]);
return 0;
}
【F】 Directing Rainfall
待补
【G】First of Her Name
题意简述:
给定一个 \(n+1\) 个点的 trie 树,根节点表示空串,非根节点表示的字符串为其父节点表示的字符串在前端加上父节点到它的边上的字符。
给定 \(k\) 个询问,每次给出一个字符串,询问 trie 树中 \(n\) 个非根节点表示的字符串中有多少个以询问串作为前缀。
题解:
对询问串的反串建立 AC 自动机,把原 trie 树在自动机上跑,每次跑到的位置在 \(\mathrm{fail}\) 树上的权值加 \(1\)。每个询问的答案就是 \(\mathrm{fail}\) 树上子树权值和。
注意 AC 自动机需要拓展转移,否则不断跳 \(\mathrm{fail}\) 会让复杂度失去保证。
子树权值和可以直接最后统计,但是我使用了树状数组,复杂度 \(\mathcal{O}(n\log n)\)
据此写出代码,复杂度 \(\mathcal{O}(n\Sigma+n\log n)\):
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
const int MN = 1000005;
int N, Q;
int h[MN], nxt[MN], to[MN], w[MN], tot;
inline void Ins(int x, int y, int z) {
nxt[++tot] = h[x], to[tot] = y, w[tot] = z, h[x] = tot;
}
char str[MN];
int ch[MN][26], fail[MN], cnt;
inline int Insert(char *str) {
int now = 0;
for (int i = 0; str[i]; ++i) {
int c = str[i] - 'A';
if (!ch[now][c]) ch[now][c] = ++cnt;
now = ch[now][c];
} return now;
}
std::vector<int> G[MN];
int que[MN], l, r;
void BuildAC() {
fail[0] = -1;
que[l = r = 1] = 0;
while (l <= r) {
int u = que[l++];
for (int j = 0; j < 26; ++j) {
if (ch[u][j]) {
int to = fail[u];
while (~to && !ch[to][j]) to = fail[to];
fail[ch[u][j]] = ~to ? ch[to][j] : 0;
que[++r] = ch[u][j];
}
else ch[u][j] = ~fail[u] ? ch[fail[u]][j] : 0;
}
}
for (int i = 1; i <= cnt; ++i) G[fail[i]].push_back(i);
}
int ldf[MN], rdf[MN], dfc;
void DFS0(int u) {
ldf[u] = ++dfc;
for (auto v : G[u]) DFS0(v);
rdf[u] = dfc;
}
int b[MN];
inline void Mdf(int i) { for (; i <= dfc; i += i & -i) ++b[i]; }
inline int Qur(int i) { int a = 0; for (; i; i -= i & -i) a += b[i]; return a; }
void Solve(int u, int now) {
Mdf(ldf[now]);
for (int i = h[u]; i; i = nxt[i])
Solve(to[i], ch[now][w[i]]);
}
int Pos[MN];
int main() {
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; ++i) {
int f; char ch[3];
scanf("%s%d", ch, &f);
Ins(f, i, *ch - 'A');
}
for (int i = 1; i <= Q; ++i) {
scanf("%s", str);
std::reverse(str, str + strlen(str));
Pos[i] = Insert(str);
}
BuildAC();
DFS0(0);
Solve(0, 0);
for (int i = 1; i <= Q; ++i)
printf("%d\n", Qur(rdf[Pos[i]]) - Qur(ldf[Pos[i]] - 1));
return 0;
}
【H】Hobson's Trains
题意简述:
给定一个 \(n\) 个点的图,每个点 \(i\) 都有且仅有一条连向 \(d_i\) 的有向边,即一个基环内向森林。
给定 \(k\),对于每个点,回答沿着有向边走不超过 \(k\) 步能到达这个点的点数。
题解:
找出环,对于环里面的树和环本身分开考虑。
对于树的部分,可以树上差分,DFS 解决。
对于环的部分,考虑每个点对环的贡献,每个点影响到的点是环上的一个连续段,具体位置和长度有关它到达环上的第一个点和到环的距离。
也用差分-前缀和方法统计即可。
虽然思路很简单,细节还是有一些的。
据此写出代码,复杂度 \(\mathcal{O}(n)\)。
#include <cstdio>
#include <vector>
const int MN = 500005;
int N, K, d[MN];
int inc[MN], vis[MN], ist[MN], stk[MN], tp;
int cid, Len[MN], Id[MN];
std::vector<int> C[MN], sum[MN];
void Circ(int u) {
stk[++tp] = u, vis[u] = tp, ist[u] = 1;
if (!vis[d[u]]) Circ(d[u]);
else if (ist[d[u]]) {
++cid;
for (int i = vis[d[u]]; i <= tp; ++i)
C[cid].push_back(stk[i]),
inc[stk[i]] = cid,
Id[stk[i]] = (int)C[cid].size() - 1;
Len[cid] = C[cid].size();
sum[cid].resize(Len[cid]);
}
--tp, ist[u] = 0;
}
std::vector<int> G[MN];
int tc[MN], dep[MN], S[MN];
void DFS(int u) {
stk[++tp] = u, dep[u] = tp;
tc[u] = tc[d[u]];
++S[u];
if (tp > K + 1) --S[stk[tp - K - 1]];
for (auto v : G[u]) DFS(v), S[u] += S[v];
--tp;
}
int main() {
scanf("%d%d", &N, &K);
for (int i = 1; i <= N; ++i) scanf("%d", &d[i]);
for (int i = 1; i <= N; ++i) if (!vis[i]) Circ(i);
for (int i = 1; i <= N; ++i) if (!inc[d[i]]) G[d[i]].push_back(i);
for (int i = 1; i <= N; ++i) if (inc[i]) tc[i] = i;
for (int i = 1; i <= N; ++i) if (!inc[i] && inc[d[i]]) DFS(i);
for (int i = 1; i <= N; ++i) {
if (dep[i] > K) continue;
if (K - dep[i] + 1 >= Len[inc[tc[i]]])
++sum[inc[tc[i]]][0];
else {
++sum[inc[tc[i]]][Id[tc[i]]];
int ed = (Id[tc[i]] + K - dep[i] + 1) % Len[inc[tc[i]]];
--sum[inc[tc[i]]][ed];
if (ed < Id[tc[i]]) ++sum[inc[tc[i]]][0];
}
}
for (int id = 1; id <= cid; ++id) {
S[C[id][0]] = sum[id][0];
for (int i = 1; i < Len[id]; ++i)
sum[id][i] += sum[id][i - 1],
S[C[id][i]] = sum[id][i];
}
for (int i = 1; i <= N; ++i) printf("%d\n", S[i]);
return 0;
}
【I】Karel the Robot
待补
【J】Miniature Golf
待补
【K】Traffic Blights
待补
ICPC World Finals 2019 题解的更多相关文章
- LOJ6583 ICPC World Finals 2019何以伊名始(广义后缀自动机)
对trie建SAM,询问串倒过来跑到的节点的|right|即为答案. #include<bits/stdc++.h> using namespace std; #define ll lon ...
- The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力)
The Preliminary Contest for ICPC Asia Shanghai 2019 C Triple(FFT+暴力) 传送门:https://nanti.jisuanke.com/ ...
- ACM - ICPC World Finals 2013 C Surely You Congest
原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 题目翻译: 试题来源 ACM/ICPC World Fin ...
- Hello 2019题解
Hello 2019题解 题解 CF1097A [Gennady and a Card Game] map大法好qwq 枚举每一个的第\(1,2\)位判是否与给定的重复即可 # include < ...
- The Preliminary Contest for ICPC Asia Nanjing 2019/2019南京网络赛——题解
(施工中……已更新DF) 比赛传送门:https://www.jisuanke.com/contest/3004 D. Robots(期望dp) 题意 给一个DAG,保证入度为$0$的点只有$1$,出 ...
- World Tour Finals 2019 D - Distinct Boxes 题解
太神了,专门写一篇题解 qwq 简要题意:给你 \(R\) 个红球和 \(B\) 个蓝球,你要把它们放到 \(K\) 个箱子里,要求没有两个箱子完全相同(即两种球个数就相同),求 \(K\) 的最大值 ...
- ICPC Mid-Central USA Region 2019 题解
队友牛逼!带我超神!蒟蒻的我还是一点一点的整理题吧... Dragon Ball I 这个题算是比较裸的题目吧....学过图论的大概都知道应该怎么做.题目要求找到七个龙珠的最小距离.很明显就是7个龙珠 ...
- ACM - ICPC World Finals 2013 F Low Power
原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 题目翻译: 问题描述 有n个机器,每个机器有2个芯片,每个 ...
- ACM - ICPC World Finals 2013 A Self-Assembly
原题下载 : http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 这道题其实是2013年我AC的第一道题,非常的开心,这 ...
随机推荐
- Leonardo's Notebook UVALive - 3641(置换)
题意: 给出26个大写字母的置换B,问是否存在一个置换A,使得A2 = B 解析: 两个长度为n的相同循环相乘,1.当n为奇数时结果也是一个长度为n的循环:2. 当n为偶数时分裂为两个长度为n/2 ( ...
- JavaScript 数据类型检测总结
JavaScript 数据类型检测总结 原文:https://blog.csdn.net/q3254421/article/details/85483462 在js中,有四种用于检测数据类型的方式,分 ...
- python3网络爬虫(2.1):爬取堆糖美女
额,明明记得昨晚存了草稿箱,一觉醒来没了,那就简写点(其实是具体怎么解释我也不太懂/xk,纯属个人理解,有错误还望指正) 环境: 版本:python3 IDE:pycharm2017.3.3 浏览器: ...
- 说 AppbarLayout,如何理解可折叠 Toolbar 的定制
Material Design 是个好东西,它的出现使得 Android 也能定制高颜值的界面,并且指导了如果实现复杂炫丽的交互效果,而 android Surpport Desgin 这个支持包就是 ...
- 洛谷 P3253 [JLOI2013]删除物品 解题报告
P3253 [JLOI2013]删除物品 题目描述 箱子再分配问题需要解决如下问题: (1)一共有\(N\)个物品,堆成\(M\)堆. (2)所有物品都是一样的,但是它们有不同的优先级. (3)你只能 ...
- Spring实现文件的上传下载
背景:之前一直做的是数据库的增删改查工作,对于文件的上传下载比较排斥,今天研究了下具体的实现,发现其实是很简单.此处不仅要实现单文件的上传,还要实现多文件的上传. 单文件的下载知道了,多文件的下载呢? ...
- XML模块(二十四)
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代, 大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统 ...
- 一个小时学会Oracle数据库
一.数据库概要 二.Oracle简介 三.安装运行Oracle数据库 四.使用GUI操作MySQL 五.使用SQL访问Oracle数据库 六.下载程序.帮助.视频 Oracle视频下载(111班):h ...
- Hadoop原生态版安装
1 安装环境和必要软件 3 台 ubuntu 16.04 vim 2 创建hadoop用户 sudo useradd -m hadoop -s /bin/bash sudo passwd hadoop ...
- Datatables 完整的datatables案例
这里只做收集网上一些很棒的博客!!!真的是很棒!!! https://www.cnblogs.com/luckychan/articles/6160934.html