zoukankan      html  css  js  c++  java
  • 九省联考 2018 简要题解

    从这里开始

    • 题目请移至 loj 查看

      每日憨批 ($infty / 1$)。感觉自己离滚蛋不远了。

    Day 1

    Problem A 一双木棋

       dp 即可

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    typedef vector<int> vec;
    
    const int S = 1 << 20;
    const int inf = (~0u >> 2);
    
    int n, m;
    
    int encode(const vec& a) {
    	int p = m, s = 0;
    	for (int i = 0; i < n; i++) {
    		while (p > a[i])
    			s = s << 1, p--;
    		s = s << 1 | 1;
    	}
    	while (p)
    		s <<= 1, p--;
    	return s;
    }
    vec decode(int s) {
    	vec rt;
    	rt.reserve(n);
    	int p = m;
    	for (int i = n + m - 1; ~i; i--) {
    		if ((s >> i) & 1) {
    			rt.push_back(p);
    		} else {
    			p--;
    		}
    	}
    	return rt;
    }
    
    int f[S];
    bitset<S> vis;
    int A[2][12][12];
    
    int dp(int s) {
    	if (vis[s])
    		return f[s];
    	vis[s] = true;
    	f[s] = -inf;
    	vec v = decode(s);
    	int sum = 0;
    	for (auto x : v)
    		sum += x;
    	if (sum == n * m)
    		return 0;
    	int ls = m;
    	for (int i = 0; i < n; i++) {
    		if (v[i] ^ ls) {
    			vec nv = v;
    			nv[i]++;
    			ls = v[i];
    			f[s] = max(f[s], A[sum & 1][i][v[i]] - dp(encode(nv)));
    		}
    	}
    	return f[s];
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 0; i < 2; i++) {
    		for (int x = 0; x < n; x++) {
    			for (int y = 0; y < m; y++) {
    				scanf("%d", A[i][x] + y);
    			}
    		}
    	}
    	int ans = dp(encode(vec(n, 0)));
    	printf("%d
    ", ans);
    	return 0;
    }

    Problem B IIIDX

       有相等的情形相当于由若干个要在某一个前缀中选出若干个的要求。这个可以用 Hall 定理判。它大概是后缀 min 大于等于 0。这个限制显然能用线段树维护。

    Code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    using namespace std;
    typedef bool boolean;
    
    const int N = 5e5 + 5;
    
    typedef class SegTreeNode {
        public:
            int mv, tg;
            SegTreeNode *l, *r;
    
            void pushUp() {
                mv = (l->mv < r->mv) ? (l->mv) : (r->mv);
            }
    
            void pushDown() {
                l->mv -= tg, l->tg += tg;
                r->mv -= tg, r->tg += tg;
                tg = 0;
            }
    }SegTreeNode;
    
    SegTreeNode pool[N << 2];
    SegTreeNode *top = pool;
    
    SegTreeNode* newnode(int mv) {
        top->mv = mv, top->tg = 0;
        top->l = top->r = NULL;
        return top++;
    }
    
    typedef class SegTree {
        public:
            int n;
            SegTreeNode* rt;
    
            SegTree() {    }
            SegTree(int n):n(n) {
                build(rt, 1, n);
            }
    
            void build(SegTreeNode*& p, int l, int r) {
                p = newnode(l);
                if (l == r)
                    return ;
                int mid = (l + r) >> 1;
                build(p->l, l, mid);
                build(p->r, mid + 1, r);
            }
    
            void update(SegTreeNode* p, int l, int r, int ql, int qr, int val) {
                if (l == ql && r == qr) {
                    p->mv -= val, p->tg += val;
                    return;
                }
                if (p->tg)
                    p->pushDown();
                int mid = (l + r) >> 1;
                if (qr <= mid)
                    update(p->l, l, mid, ql, qr, val);
                else if (ql > mid)
                    update(p->r, mid + 1, r, ql, qr, val);
                else {
                    update(p->l, l, mid, ql, mid, val);
                    update(p->r, mid + 1, r, mid + 1, qr, val);
                }
                p->pushUp();
            }
    
            int query(SegTreeNode* p, int l, int r, int len) {
                if (l == r)
                    return (p->mv >= len) ? (l) : (l + 1);
                if (p->tg)
                    p->pushDown();
                int mid = (l + r) >> 1;
                if (p->r->mv >= len)
                    return query(p->l, l, mid, len);
                return query(p->r, mid + 1, r, len);
            }
    
            int query(int len) {
                return query(rt, 1, n, len);
            }
    
            void update(int l, int r, int val) {
                update(rt, 1, n, l, r, val);
            }
    }SegTree;
    
    int n;
    double K;
    int fa[N], ds[N];
    int siz[N], cnt[N], ans[N];
    SegTree st;
    
    inline void init() {
        scanf("%d%lf", &n, &K);
        for (int i = 1; i <= n; i++)
            scanf("%d", ds + i);
        sort(ds + 1, ds + n + 1, greater<int>());
        for (int i = 1; i <= n; i++)
            fa[i] = i / K;
        for (int i = n; i; i--)
            siz[fa[i]] += (++siz[i]);
        cnt[n] = 1;
        for (int i = n - 1; i; i--)
            cnt[i] = (ds[i] == ds[i + 1]) ? (cnt[i + 1] + 1) : (1);
    }
    
    inline void solve() {
        st = SegTree(n);
        for (int i = 1, r; i <= n; i++) {
            if (i > 1 && fa[i] != fa[i - 1])
                st.update(ans[fa[i]], n, -siz[fa[i]] + 1);
            r = st.query(siz[i]);
            ans[i] = r + (--cnt[r]);
            st.update(ans[i], n, siz[i]);
        }
        for (int i = 1; i <= n; i++)
            printf("%d ", ds[ans[i]]);
    }
    
    int main() {
        init();
        solve();
        return 0;
    }

    Problem C 秘密袭击

      常规做法是枚举一个点,算它是第 $K$ 大的方案数。然后这个涉及到每次把一个点的重量从 0 变成 1。换根相当于是要求合并链上所有前缀背包的和或者后缀背包的和。这个可以把生成函数用点值表示,然后全局平衡二叉树瞎维护一下就行了。

      标算做法大概是考虑计算大于等于 $v$ 中的点选了 $j$ 个,然后对于每个点来说是一个区间加物品。现在要求合并子节点信息,以及区间加物品。后者用线段树维护,前者可以线段树合并。实际还有很多细节要处理,此处省略各种细节一万字。

      然而你笑道,正解无用,暴力把分送。

      说了这么多,还是暴力好写。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 1670;
    const int Mod = 64123;
    
    #define ll long long
    
    void inc(ll& x) {
    	if (x >= Mod)
    		x -= Mod;
    }
    
    int n, K;
    ll f[N][N];
    int a[N], id[N];
    int sz[N], fa[N];
    vector<int> G[N];
    
    bitset<N> vis;
    void recalc(int p) {
    	for (int i = 0; i <= K && i <= sz[p]; i++)
    		f[p][i] = 0;
    	f[p][0] = 1;
    	int sum = 0;
    	for (auto e : G[p]) {
    		if (e ^ fa[p]) {
    			for (int i = min(sum, K); ~i; i--) {
    				if (!f[p][i])
    					continue;
    				for (int j = min(K - i, sz[e]); ~j; j--) {
    					f[p][i + j] += f[p][i] * f[e][j];
    				}
    			}
    			sum += sz[e];
    			for (int i = 0; i <= K && i <= sum; i++)
    				f[p][i] %= Mod;
    		}
    	}
    	if (vis.test(p)) {
    		for (int i = min(K, sum + 1); i; i--)
    			f[p][i] = f[p][i - 1];
    		f[p][0] = 0;
    	}
    }
    
    void rev(int p) {
    	if (!fa[p]) {
    		return;
    	}
    	int q = fa[p];
    	rev(q);
    	fa[q] = p;
    	fa[p] = 0;
    	sz[q] -= sz[p];
    	sz[p] += sz[q];
    	recalc(q);
    }
    
    void dfs(int p, int fa) {
    	::fa[p] = fa;
    	for (auto e : G[p]) {
    		if (e ^ fa) {
    			dfs(e, p);
    		}
    	}
    	recalc(p);
    }
    
    int main() {
    	scanf("%d%d%*d", &n, &K);
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", a + i);
    		id[i] = i;
    	}
    	for (int i = 1, u, v; i < n; i++) {
    		scanf("%d%d", &u, &v);
    		G[u].push_back(v);
    		G[v].push_back(u);
    	}
    	sort(id + 1, id + n + 1, [&] (int x, int y) {	return a[x] > a[y];	});
    	vis.set(id[1]);
    	sz[id[1]] = 1;
    	dfs(id[1], 0);
    	ll ans = f[id[1]][K] * a[id[1]];
    	for (int i = 2; i <= n; i++) {
    		int p = id[i];
    		vis.set(p);
    		rev(p);
    		sz[p]++;
    		recalc(p);
    		ans += f[p][K] * a[p];
    	}
    	printf("%lld
    ", ans % Mod);
    	return 0;
    }

    Day 2

    Problem A 劈配

      第一问不断加边暴力增广。

      第二问相当于询问第 $i$ 个人只加入志愿在 $[1, s_i]$ 的边,在匈牙利树上到达左侧小于 $i$ 的点中的点权的最大值。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 205;
    
    int T, n, m;
    int cap[N], ans[N];
    vector<int> mat[N];
    vector<int> a[N][N];
    vector<int> G[N];
    boolean visL[N], visR[N];
    
    boolean augment(int p) {
    	if (visL[p])
    		return false;
    	visL[p] = true;
    	for (auto e : G[p]) {
    		if (visR[e])
    			continue;
    		visR[e] = true;
    		if ((signed) mat[e].size() < cap[e]) {
    			mat[e].push_back(p);
    			return true;
    		}
    		for (auto& np : mat[e]) {
    			if (augment(np)) {
    				np = p;
    				return true;
    			}
    		}
    	}
    	return false;
    }
    
    void solve() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; i++) {
    		scanf("%d", cap + i);
    	}
    	cap[m + 1] = 233;
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1, x; j <= m; j++) {
    			scanf("%d", &x);
    			if (x)
    				a[i][x].push_back(j);
    		}
    		a[i][m + 1].push_back(m + 1);
    	}
    	
    	for (int i = 1; i <= n; i++) {
    		fill(visL + 1, visL + n + 1, false);
    		fill(visR + 1, visR + m + 2, false);
    		for (int j = 1; j <= m + 1; j++) {
    			G[i] = a[i][j];
    			visL[i] = false;
    			if (augment(i)) {
    				ans[i] = j;
    				break;
    			}
    		}
    	}
    	for (int i = 1; i <= n; i++) {
    		printf("%d ", ans[i]);
    	}
    	putchar('
    ');
    	
    	for (int i = 1; i <= n; i++) {
    		G[i].clear();
    	}
    	for (int i = 1; i <= m + 1; i++) {
    		mat[i].clear();
    	}
    	for (int i = 1, s; i <= n; i++) {
    		scanf("%d", &s);
    		int ans1 = 0;
    		if (s >= ans[i]) {
    			ans1 = i;
    		} else {
    			for (int j = 1; j <= s; j++) {
    				for (auto e : a[i][j]) {
    					G[i].push_back(e);
    				}
    			}
    			fill(visL + 1, visL + n + 1, false);
    			fill(visR + 1, visR + m + 1, false);
    			augment(i);
    			for (int j = 1; j < i; j++) {
    				if (visL[j]) {
    					ans1 = max(ans1, j);
    				}
    			}
    		}
    		printf("%d ", i - ans1);
    		fill(visL + 1, visL + n + 1, false);
    		fill(visR + 1, visR + m + 1, false);
    		G[i] = a[i][ans[i]];
    		augment(i);
    	}
    	putchar('
    ');
    
    	for (int i = 1; i <= n; i++) {
    		G[i].clear();
    	}
    	for (int i = 1; i <= m + 1; i++) {
    		mat[i].clear();
    	}
    	for (int i = 1; i <= n; i++) {
    		for (int j = 1; j <= m + 1; j++) {
    			a[i][j].clear();
    		}
    	}
    }
    
    int main() {
    	scanf("%d%*d", &T);
    	while (T--) {
    		solve();
    	}
    	return 0;
    }

    Problem B 林克卡特树

      不难把问题转化成选 $K + 1$ 条点不相交的链,最大化边权和。

      凸优化,dp 即可。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    #define ll long long
    #define pii pair<int, int>
    #define pli pair<ll, int>
    #define fi first
    #define sc second
    
    template <typename T>
    boolean vmax(T& a, T b) {
    	return (a < b) ? (a = b, true) : false;
    }
    template <typename T>
    T smax(T a) {
    	return a;
    }
    template <typename T, typename ...K>
    T smax(T a, const K& ...args) {
    	return max(a, smax(args...));
    }
    
    const int N = 3e5 + 5;
    const ll llf = 3e18;
    
    pli operator + (pli a, pli b) {
    	return pli(a.first + b.first, a.second + b.second);
    }
    
    int n, K;
    vector<pii> G[N];
    
    pli f[N][4];
    void dfs(int p, int fa, ll mid) {
    	static pli h[4];
    	pli *g = f[p];
    	g[0] = pli(0, 0);
    	g[1] = pli(-llf, 0);
    	g[2] = pli(-llf, 0);
    	g[3] = pli(0, 0);
    	for (auto _ : G[p]) {
    		int e = _.first;
    		int w = _.second;
    		if (e == fa)
    			continue;
    		dfs(e, p, mid);
    		pli *t = f[e];
    		h[0] = h[1] = h[2] = h[3] = pli(-llf, 0);
    		pli v1 = max(smax(t[0], t[1], t[2]) + pli(mid, 1), t[3]);
    		pli v2 = max(t[0], t[1]) + pli(w, 0);
    		vmax(h[0], g[0] + v1);
    		vmax(h[1], g[0] + v2);
    		vmax(h[1], g[1] + v1);
    		vmax(h[2], g[2] + v1);
    		vmax(h[2], g[1] + v2);
    		vmax(h[3], g[3] + v1);
    		g[0] = h[0], g[1] = h[1];
    		g[2] = h[2], g[3] = h[3];
    	}
    }
    	
    
    int main() {
    	scanf("%d%d", &n, &K);
    	++K;
    	for (int i = 1, u, v, w; i < n; i++) {
    		scanf("%d%d%d", &u, &v, &w);
    		G[u].emplace_back(v, w);
    		G[v].emplace_back(u, w);
    	}
    	ll l = -3e11, r = 3e11, mid;
    	while (l <= r) {
    		mid = (l + r) >> 1;
    		dfs(1, 0, mid);
    		pli* t = f[1];
    		pli res = max(smax(t[0], t[1], t[2]) + pli(mid, 1), t[3]);
    		if (res.sc < K) {
    			l = mid + 1;
    		} else {
    			r = mid - 1;
    		}
    	}
    	dfs(1, 0, l);
    	pli* t = f[1];
    	pli res = max(smax(t[0], t[1], t[2]) + pli(l, 1), t[3]);
    	ll ans = res.fi - K * l;
    	printf("%lld
    ", ans);
    	return 0;
    }

    Problem C 制胡窜

      发现很久没写线段树和后缀自动机,于是抱着写写板子的心态来写写这个题,于是又荒废了一个晚上。

      一个没什么意思的讨论题。

      假设你处理出了 right 集合。我们设三段字符串分别为 $s_1, s_2, s_3$

    • $s_1$ 中包含 $s$
    • $s_3$ 中包含 $s$
    • $s_1, s_3$ 中同时包含 $s$
    • $s_2$ 中包含 $s$,但 $s_1, s_3$ 都不包含
      • $s$ 第一次出现没有被截断,最后一次出现也没有被截断
      • $s$ 第一次出现被截断,最后一次出现没有被截断
      • $s$ 第一次没有出现被截断,最后一次出现被截断
      • $s$ 第一次出现和最后一次出现都被截断

      最后一项要用 border 长度形成 log 个等差数列的性质吗?不不不,冷静一下,它只用维护相邻两项的差乘后一项的乘积,这个线段树瞎维护一下就行了。

      实际还有很多细节要处理,此处省略各种细节一万字。

    Code

    /**
     * Copy the templates and you'll get AC.
     * Please stop writing problems!
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 1e5 + 5;
    const int bzmax = 18;
    
    #define ll long long
    
    int n, m;
    
    typedef class Data {
    	public:
    		int mi, mx;
    		ll sum;
    
    		Data() {	}
    		Data(int x) : mi(x), mx(x), sum(0) {	} 
    		Data(int mi, int mx, ll sum) : mi(mi), mx(mx), sum(sum) {	}
    
    		boolean empty() {
    			return mi > mx;
    		}
    		Data operator + (Data b) {
    			if (empty())
    				return b;
    			if (b.empty())
    				return *this;
    			return Data(mi, b.mx, sum + b.sum + (b.mi - mx) * 1ll * b.mi);
    		}
    } Data;
    
    Data dnul (N, -1, 0);
    
    typedef class SegTreeNode {
    	public:
    		Data d;
    		SegTreeNode *l, *r;
    		
    		SegTreeNode() : d(N, -1, 0) {	}
    		SegTreeNode(SegTreeNode* slf) : l(slf), r(slf) {	}
    		
    		void push_up() {
    			d = dnul;
    			if (l) d = l->d;
    			if (r) d = d + r->d;
    		}
    } SegTreeNode;
    
    SegTreeNode pool[N * 40];
    SegTreeNode *_top1 = pool;
    
    void insert(SegTreeNode* &p, int l, int r, int idx, const Data& v) {
    	p = _top1++;
    	if (l == r) {
    		p->d = v;		
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if (idx <= mid) {
    		insert(p->l, l, mid, idx, v);
    	} else {
    		insert(p->r, mid + 1, r, idx, v);
    	}
    	p->push_up();
    }
    void insert(SegTreeNode*& p, int idx, const Data& v) {
    	insert(p, 1, n, idx, v);
    }
    
    Data query(SegTreeNode* p, int l, int r, int ql, int qr) {
    	if (!p)
    		return dnul;
    	if (l == ql && r == qr)
    		return p->d;
    	int mid = (l + r) >> 1;
    	if (qr <= mid) {
    		return query(p->l, l, mid, ql, qr);
    	} else if (ql > mid) {
    		return query(p->r, mid + 1, r, ql, qr);
    	}
    	return query(p->l, l, mid, ql, mid) + query(p->r, mid + 1, r, mid + 1, qr);
    }
    Data query(SegTreeNode* p, int l, int r) {
    	return query(p, 1, n, l, r);
    }
    
    void merge(SegTreeNode* a, SegTreeNode* b, SegTreeNode* &p) {
    	if (!a) {
    		p = b;
    		return;
    	}
    	if (!b) {
    		p = a;
    		return;
    	}
    	p = _top1++;
    	merge(a->l, b->l, p->l);
    	merge(a->r, b->r, p->r);
    	p->push_up();
    }
    
    inline int cti(char x) {
    	return x - '0';
    }
    
    typedef class TrieNode {
    	public:
    		int len;
    		TrieNode* par;
    		TrieNode* ch[10];
    		TrieNode* bz[bzmax];
    		SegTreeNode* rt;
    
    		void init_bz() {
    			if (!par) {
    				bz[0] = this;
    			} else {
    				bz[0] = par;
    			}
    			for (int i = 1; i < bzmax; i++)
    				bz[i] = bz[i - 1]->bz[i - 1];
    		}
    		void insert(int x) {
    			::insert(rt, x, Data(x));
    		}
    		TrieNode* jump(int nlen) {
    			TrieNode* p = this;
    			for (int i = bzmax - 1; ~i; i--) {
    				if (p->bz[i]->len >= nlen) {
    					p = p->bz[i];
    				}
    			}
    			return p;
    		}
    } TrieNode;
    
    TrieNode pool1[N << 1];
    TrieNode* _top = pool1;
    
    TrieNode* newnode(int len) {
    	_top->len = len;
    	return _top++;
    }
    
    typedef class SuffixAutomaton {
    	public:
    		TrieNode *rt, *lst;
    
    		SuffixAutomaton() : rt(newnode(0)), lst(rt) {	}
    
    		TrieNode* extend(char _) {
    			int c = cti(_);
    			TrieNode* p = newnode(lst->len + 1);
    			TrieNode* f = lst;
    			while (f && !f->ch[c])
    				f->ch[c] = p, f = f->par;
    			if (!f) {
    				p->par = rt;
    			} else {
    				TrieNode* q = f->ch[c];
    				if (q->len == f->len + 1) {
    					p->par = q;
    				} else {
    					TrieNode* nq = newnode(f->len + 1);
    					memcpy(nq->ch, q->ch, sizeof(nq->ch));
    					nq->par = q->par, q->par = p->par = nq;
    					while (f && f->ch[c] == q)
    						f->ch[c] = nq, f = f->par;
    				}
    			}
    			return lst = p;
    		}
    
    		vector<TrieNode*> order;
    		void topusort() {
    			vector<int> cnt (lst->len + 1, 0);
    			for (TrieNode* p = pool1; p != _top; p++)
    				cnt[p->len]++;
    			for (int i = 1; i < (signed) cnt.size(); i++)
    				cnt[i] += cnt[i - 1];
    			order.resize(_top - pool1);
    			for (TrieNode* p = pool1; p != _top; p++)
    				order[--cnt[p->len]] = p;
    		}
    } SuffixAutomaton;
    
    char s[N];
    TrieNode* nodes[N];
    SuffixAutomaton sam;
    
    ll Cn2(int n) {
    	if (n < 3)
    		return 0;
    	return 1ll * n * (n - 1) / 2 - (n - 1);
    }
    
    ll solve(SegTreeNode* st, int len) {
    	assert(st);
    	Data dall = st->d;
    	int L = dall.mi, R = dall.mx;
    	ll ansL = Cn2(n - (L + len - 1) + 1);
    	ll ansR = Cn2(R);
    	ll ansM00 = 1ll * (L - 1) * (n - (R + len - 1));
    	if (L == R)
    	   return ansL + ansR + ansM00;
    	ll ansLR = Cn2(R - L - len + 2);
    	ll ansM01 = 1ll * (L - 1) * (min(len - 1, R - L));
    	ll ansM10 = 1ll * (min(len - 1, R - L)) * (n - (R + len - 1));
    	ll ansM11 = 0;
    	Data d = query(st, max(1, R - len + 1), n);
    	int M = d.mi, Led = L + len - 1;
    	if (M == L) {
    		ansM11 = 1ll * (R - L) * R - d.sum;
    	} else {
    		int pr = query(st, L, M - 1).mx;
    		if (pr < Led) {
    			ansM11 = 1ll * (pr - L) * (len - 1) + 1ll * (Led - pr) * R;
    			d = query(st, pr, Led - 1);
    			ansM11 -= d.sum + 1ll * (Led - d.mx) * query(st, Led, R).mi;
    		} else {
    			ansM11 = 1ll * (len - 1) * (len - 1);
    		}
    	}
    	return ansL + ansR - ansLR + ansM00 + ansM01 + ansM10 + ansM11;
    }
    ll solve(int l, int r) {
    	TrieNode* p = nodes[l]->jump(r - l + 1);
    	return solve(p->rt, r - l + 1);
    }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	scanf("%s", s + 1);
    	for (int i = n; i; i--)
    		nodes[i] = sam.extend(s[i]);
    	sam.topusort();
    	auto& V = sam.order;
    	for (auto p : V)
    		p->init_bz();
    	reverse(V.begin(), V.end());
    	for (int i = 1; i <= n; i++)
    		nodes[i]->insert(i);
    	for (auto p : V) {
    		if (p->par) {
    			merge(p->par->rt, p->rt, p->par->rt);
    		}
    	}
    	while (m--) {
    		int l, r;
    		scanf("%d%d", &l, &r);
    		printf("%lld
    ", solve(l, r));
    	}
    	return 0;
    }

  • 相关阅读:
    easyui控件写法造成的错误
    外部访问服务器数据库被防火墙屏蔽报错
    云服务器Windows Server2012 配置http服务器(又称Web服务器,IIS)
    mysql五:索引原理与慢查询优化
    mysql四:数据操作
    mysql四-2:多表查询
    sql查询作业答案
    mysql四-1:单表查询
    mysql五补充部分:SQL逻辑查询语句执行顺序
    第三篇:表操作
  • 原文地址:https://www.cnblogs.com/yyf0309/p/12459603.html
Copyright © 2011-2022 走看看