• The 2018 ACM-ICPC Asia Beijing Regional Contest


    Contest Info


    [Practice Link](https://vjudge.net/contest/334680)
    Solved A B C D E F G H I J
    6/10 O O - O - Ø - O O -
    • O 在比赛中通过
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    A - Jin Yong’s Wukong Ranking List

    题意:
    给出(n)对有向关系,判断前多少对关系会形成一个环。

    思路:
    慢慢加入一对关系,跑拓扑排序,出现环就停止。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 510;
    int n;
    map<string, int> mp; int tot;
    string s[2][N];
    int getid(string s) {
    	if (mp.count(s)) return mp[s];
    	mp[s] = ++tot;
    	return mp[s];
    }
    
    vector <vector<int>> G;
    int d[N];
    bool gao(int n) {
    	memset(d, 0, sizeof d);
    	G.clear(); G.resize(tot + 1);
    	for (int i = 1; i <= n; ++i) {
    		int u = getid(s[0][i]), v = getid(s[1][i]);
    		++d[v];
    		G[u].push_back(v);
    	}
    	int cnt = 0;
    	queue <int> que; 
    	for (int i = 1; i <= tot; ++i) if (!d[i]) que.push(i);
    	while (!que.empty()) {
    		int u = que.front(); que.pop();
    		++cnt;
    		for (auto &v : G[u]) {
    			if (--d[v] == 0) {
    				que.push(v);
    			}
    		}
    	}
    	return cnt != tot;
    }
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0); cout.tie(0);
    	while (cin >> n) {
    		mp.clear(); tot = 0;
    		for (int i = 1; i <= n; ++i) {
    			cin >> s[0][i] >> s[1][i];
    			getid(s[0][i]); getid(s[1][i]);
    		}
    		bool flag = 0;
    		for (int i = 1; i <= n; ++i) {
    			if (gao(i)) {
    				cout << s[0][i] << " " << s[1][i] << "
    ";
    				flag = 1;
    				break;
    			}
    		}
    		if (!flag) cout << 0 << "
    ";
    	}
    	return 0;
    }
    

    B - Heshen's Account Book

    题意:
    模拟题。给出若干行文本,包含空格、数字、字母。
    找出其中所有连续的自然数,但是如果某一行的结尾是数字,并且下一行的开头也是数字,那么这两行的数字视为连在一起的。

    思路:
    直接将所有行连在一起再判断。
    能直接连就直接连,不能直接连中间加一个空格。
    不要分类讨论做,很多Case考虑不到。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    using pSI = pair<string, int>;
    #define fi first
    #define se second
    const int N = 2e5 + 10;
    int vis[N], num[N], pos, len;
    string s, t;
    bool isnum(string &s) {
    	int len = s.size();
    	if (len == 1) {
    		return isdigit(s[0]);
    	} 
    	if (s[0] == '0') return false;
    	for (int i = 0; i < len; ++i)
    		if (!isdigit(s[i]))
    			return false;
    	return true;
    }
    
    pSI get() {
    	pSI tmp = pSI("", -1);
    	while (pos < len && t[pos] == ' ') ++pos;
    	while (pos < len && t[pos] != ' ') {
    		if (tmp.se == -1) tmp.se = vis[pos];
    		tmp.fi += t[pos];
    		++pos; 
    	}
    	return tmp;
    }
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin.tie(0); cout.tie(0);
    	memset(vis, 0, sizeof vis);
    	memset(num, 0, sizeof num);
    	s = t = "";
    	int pre = -1; t = "";
    	int n = 0;
    	while (getline(cin, s)) {
    		++n;
    		if (!t.empty() && isdigit(t.end()[-1]) && isdigit(s[0])) {
    			t += s;
    		} else {
    			t += " ";
    			t += s;
    			++pre;
    		}
    		int len = t.size();
    		for (int i = pre + 1; i < len; ++i) vis[i] = n;
    	   	pre = len - 1;	
    	} 
    	len = t.size();
    	pos = 0;
    	vector <string> vec;
    	while (1) {
    		pSI tmp = get();
    		if (tmp.se == -1) break;
    		if (isnum(tmp.fi)) {
    			++num[tmp.se];
    			vec.push_back(tmp.fi);
    		}
    	}
    	int sze = vec.size();
    	for (int i = 0; i < sze; ++i)
    		cout << vec[i] << " 
    "[i == sze - 1];
    	for (int i = 1; i <= n; ++i)
    		cout << num[i] << "
    ";
    	return 0;
    }
    

    C - Pythagorean triple

    题意:
    统计有多少个三元组((a, b, c))使得(a^2 + b^2 = c^2),并且满足(c leq N)

    D - Frog and Portal

    题意:
    现在有(201)个点,可以加一些传送门,一旦进入这个点就会被传送到另一个点。
    现在青蛙在(0)号点,它要到(200)号点,它如果在(p)号点,那么下一步可以去(p + 1)或者(p + 2)号点。
    问你如何加传送门,使得它从(0)(200)的方案数是(m)

    思路:
    考虑不加任何传送门方案数是第(201)个斐波那契数。
    我们可以考虑任意一个整数都可以被分解为若干个斐波那契数相加。
    那么我们从起点的某个地方直接传送到(201 - x)那个点,那么从(x)(201)的方案数就是第(x)个斐波那契数。
    并且控制从(0)走到那个传送门的方案为(1)即可。

    代码:

    view code
    #include <bits/stdc++.h>
    
    using namespace std;
    
    using ll = long long;
    
    const int N = 110;
    
    ll m;
    ll f[N];
    int a[N];
    
    int main() {
    	f[0] = 1, f[1] = 1;
    	for (int i = 2; i <= 50; ++i) {
    		f[i] = f[i - 1] + f[i - 2];
    	}
    	while (scanf("%lld", &m) != EOF) {
    		if (m == 0) {
    			puts("2
    1 1
    2 1");
    			continue;
    		}
    		*a = 0;
    		for (int i = 50; i >= 1; --i) {
    			if (m >= f[i]) {
    				a[++*a] = i;
    				m -= f[i];
    			}
    		}
    		printf("%d
    ", *a + 1);
    		for (int i = 1; i <= *a; ++i) {
    			printf("%d %d
    ", 2 * i - 1, 200 - a[i]);
    		}
    		printf("%d %d
    ", 2 * (*a), 2 * (*a));
    	}
    	return 0;
    }
    

    F - The Kth Largest Value

    题意:
    给出一个有向图,定义((u, v))是好的二元组当且仅当(u)(v)至少存在一条可达路径,当然((u, u))是好的。
    现在定义二元组((u, v))的权值为(u oplus v),现在有(q)次询问,询问所有好的二元组中的第(k)大的权值。

    思路:
    先用(tarjan + topo)求出拓扑序,并且用(bitset)求出(f[u])表示(u)可以到达哪些点。
    显然有个思路是二分,然后去找有多少个(u oplus v > mid),但是这个统计可以放在(Trie)上做,所以就不用二分了。
    那么从高位到低位贪心,每次尝试当前位放(0),那么对于当前位放(1)并且低位任意放的情况都是比当前这个数要大的。
    放到字典树上就是统计子树和。
    但是我们注意到它的权值是([1, n])连续的,所以不用字典树,它是一棵完全二叉树,并且(DFS)序是确定的就是([1, n])
    那么相当于对于每个点查询一段区间和。可以用手写(bitset)维护(f[u])的前缀和,就可以(O(1))查询一段区间内(1)的个数。
    并且注意对于每个(u)来说,在它的字典树上往下走的过程要异或(u)的那一位二进制位。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    #define dbg(x...) do { cout << "33[32;1m" << #x << " -> "; err(x); } while (0) 
    void err() { cout << "33[39;0m" << endl; } 
    template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...); }
    using ll = long long;
    using ull = unsigned long long;
    const int N = 5e4 + 10, M = 2e5 + 10;
    struct Bitset {
        #define W (64)
    
        int n;
        ull bits[N / W + 10];
        int num[N / W + 10];
    
        void preWork() {
            for(int i = 0;i <= n / W; ++i) num[i] = __builtin_popcountll(bits[i]);
            for(int i = n / W - 1;i >= 0; --i) num[i] += num[i + 1];
         //  for(int i = 0;i < m;i ++) printf("%d ",sum[i]); puts("");
          //  for(int i = 0;i <= n/W;i ++) printf("%llu ",bits[i]); puts("");
        }
    
        int ask(int x) {
            if(x > n) return 0; 
            int blockid = x / W;
            int ans = __builtin_popcountll(bits[blockid]>>(x%W));
            blockid++;
            if(blockid <= n/W) ans += num[blockid];
            return ans;
        }
    
        int ask(int l,int r) {
    		if (l > r) return 0;
            return ask(l) - ask(r+1);
        }
    
    	void Xor(const Bitset &t) {
    		for (int i = 0; i <= n / W; ++i) bits[i] ^= t.bits[i]; 
    	}
    
    	void And(const Bitset &t) {
    		for (int i = 0; i <= n / W; ++i) bits[i] &= t.bits[i];
    	}
    
        void Or(const Bitset &t) {
            for(int i = 0; i <= n / W; ++i) bits[i] |= t.bits[i];
        }
    
        void Copy(const Bitset &t) {
            n = t.n;
            for(int i = 0; i <= n / W; ++i) bits[i] = t.bits[i];
        }
    
        void Set(int x) { 
            bits[x / W] |= 1llu << (x % W);
        }
    
    	void Reset(int x) {
    		Set(x);
    		bits[x / W] ^= 1llu << (x % W);
    	}
    
        void init(int _n) {
            n = _n + 1; //n++;
            for(int i = 0; i <= n / W; i++) bits[i] = 0;
        }
    
        void print() {
            for(int i = 0; i <= n; ++i) { 
                if(bits[i / W] >> (i % W) & 1) 
                    printf("%d ", i);
            }
            puts("");
        }
    
        #undef W
    }bs[N], bg[N];
    int n, m, q, f[N];
    vector <vector<int>> G; 
    struct Tarjan {
    	int Low[N], DFN[N], sta[N], Belong[N], num[N], d[N], scc; 
    	bool Insta[N];
    	void dfs(int u) {
    		Low[u] = DFN[u] = ++*Low;
    		sta[++*sta] = u;
    		Insta[u] = 1;
    		for (auto &v : G[u]) {
    			if (!DFN[v]) {
    				dfs(v);
    				Low[u] = min(Low[u], Low[v]);
    			} else if (Insta[v]) {
    				Low[u] = min(Low[u], DFN[v]);
    			}
    		}
    		if (Low[u] == DFN[u]) {
    			++scc;
    			int v;
    			do {
    				v = sta[(*sta)--];
    				Insta[v] = 0;
    				Belong[v] = scc;
    				++num[scc];
    			} while (v != u);
    		}
    	}
    	void gao() {
    		memset(DFN, 0, sizeof DFN);
    		memset(Insta, 0, sizeof Insta);
    		memset(num, 0, sizeof num);
    		memset(d, 0, sizeof d);
    		scc = *sta = *Low = 0;
    		for (int i = 1; i <= n; ++i) if (!DFN[i]) dfs(i);	
    		vector <vector<int>> H(scc + 1), bk(scc + 1);
    		for (int i = 1; i <= scc; ++i) bg[i].init(n); 
    		for (int u = 1; u <= n; ++u) {
    			bk[Belong[u]].push_back(u);
    			for (auto &v : G[u]) {
    				if (Belong[u] == Belong[v]) {
    					continue;
    				}
    				H[Belong[v]].push_back(Belong[u]);
    				++d[Belong[u]];
    			}
    		}
    		queue <int> que;
    		for (int i = 1; i <= scc; ++i) if (!d[i]) que.push(i);
    		while (!que.empty()) {
    			int u = que.front(); que.pop();
    			for (auto &it : bk[u]) {
    				bg[u].Set(it); 
    			}
    			for (auto &v : H[u]) {
    				bg[v].Or(bg[u]); 
    				if (--d[v] == 0) {
    					que.push(v);
    				}
    			}
    		}
    		for (int i = 1; i <= n; ++i) {
    			bs[i] = bg[Belong[i]]; 
    			bs[i].Set(i);
    			bs[i].preWork();
    		}
    	}
    }tarjan;
    
    int main() {
    	int _T; scanf("%d", &_T);
    	while (_T--) {
    		scanf("%d%d%d", &n, &m, &q);
    		G.clear(); G.resize(n + 1); 
    		for (int i = 1, u, v; i <= m; ++i) {
    			scanf("%d%d", &u, &v);
    			G[u].push_back(v);
    		}
    		tarjan.gao();
    	//	for (int i = 1; i <= n; ++i) bs[i].print();
    		int len = 1, cnt = 1;
    		while (len < n * 2) len <<= 1, ++cnt;  
    		while (q--) {
    			ll K; int res = 0; scanf("%lld", &K); --K; 
    			memset(f, 0, sizeof f); 
    			for (int i = cnt; i >= 0; --i) {
    				//试着放1 
    				ll num = 0;
    				int bit = 1 << i; 
    				for (int j = 1; j <= n; ++j) {
    					if (((j >> i) & 1) == 0) { 
    						f[j] |= bit; 
    					}
    					num += bs[j].ask(f[j], min(n, (f[j] | (bit - 1)))); 
    					if (((j >> i) & 1) == 0) {
    						f[j] ^= bit;
    					}
    				}
    				if (K >= num) {
    					K -= num; 
    					for (int j = 1; j <= n; ++j) {
    						if ((j >> i) & 1) {
    							f[j] |= bit;
    						}
    					}
    				} else {
    					res |= bit; 
    					for (int j = 1; j <= n; ++j) {
    						if (((j >> i) & 1) == 0) {
    							f[j] |= bit;
    						}
    					}
    				}
    			}
    			printf("%d
    ", res);
    		}
    	}
    	return 0;
    }
    

    H - Approximate Matching

    题意:
    给出一个长度为(n)的模式串,询问你有多少个长度为(m)的文本串,使得模式串可以在文本串中被匹配上。
    匹配过程中可以有一位是失配的。

    思路:
    考虑将模式串拆成(n)个不同的模式串,那么就转化成了完全匹配。
    (n)个模式串插入(AC)自动机,然后考虑(f[i][j])表示到了文本串的第(i)位,并且匹配指针到了(AC)自动机上的第(j)个结点的方案数。
    一旦匹配上了,那么后面的字符任意放直接算贡献,并且这个方案不需要转移给下一位。
    也就是说我们把贡献算在第一次匹配的位置。

    或者可以将所有没有匹配上的结点进行(dp),这样最后算出来的是不合法的方案数,拿总方案减去即可。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    const int N = 1e4 + 10, ALP = 2;
    int n, m; char s[N];
    ll f[50][N];
    struct ACAM {
    	struct node {
    		int nx[ALP], fail;
    		int cnt; 
    		node() {
    			memset(nx, -1, sizeof nx);
    			cnt = 0; 
    		}
    	}t[N];
    	int root, tot;
    	int que[N], ql, qr;
    	int newnode() {
    		++tot;
    		t[tot] = node();
    		return tot;
    	}
    	void init() {
    		tot = 0;
    		root = newnode();
    	}
    	void insert(char *s) {
    		int len = strlen(s);
    		int now = root;
    		for (int i = 0; i < len; ++i) {
    			if (t[now].nx[s[i] - '0'] == -1) 
    				t[now].nx[s[i] - '0'] = newnode();
    			now = t[now].nx[s[i] - '0'];
    		}
    		++t[now].cnt;
    	}
    	void build() {
    		ql = 1, qr = 0;
    		t[root].fail = root;
    		for (int i = 0; i < ALP; ++i) {
    			if (t[root].nx[i] == -1) {
    				t[root].nx[i] = root;
    			} else {
    				t[t[root].nx[i]].fail = root;
    				que[++qr] = t[root].nx[i];
    			}
    		}
    		while (ql <= qr) {
    			int now = que[ql++];
    			for (int i = 0; i < ALP; ++i) {
    				if (t[now].nx[i] == -1) {
    					t[now].nx[i] = t[t[now].fail].nx[i];
    				} else {
    					t[t[now].nx[i]].fail = t[t[now].fail].nx[i];
    					que[++qr] = t[now].nx[i];
    				}
    			}
    		}
    	}
    	ll gao() {
    		ll res = 0;
    		for (int i = 0; i <= m; ++i)
    			for (int j = 0; j <= tot; ++j)
    				f[i][j] = 0;
    		f[0][root] = 1;
    		for (int i = 0; i < m; ++i) {
    			for (int j = 1; j <= tot; ++j) {
    				if (t[j].cnt > 0) continue;
    				for (int k = 0; k < 2; ++k) {
    					if (t[j].nx[k] != -1) { 
    						f[i + 1][t[j].nx[k]] += f[i][j];
    						continue;
    					} 
    				}
    			}
    		}
    		for (int i = 1; i <= m; ++i) {
    			for (int j = 1; j <= tot; ++j) {
    				if (t[j].cnt > 0) {
    					res += f[i][j] * (1ll << (m - i));
    				}
    			}
    		}
    
    		return res;
    	}
    }acam;
    
    int main() {
    	int _T; scanf("%d", &_T);
    	while (_T--) {
    		scanf("%d%d%s", &n, &m, s);
    		acam.init(); acam.insert(s);
    		for (int i = 0; i < n; ++i) {
    			s[i] = ((s[i] - '0') ^ 1) + '0';
    			acam.insert(s);
    			s[i] = ((s[i] - '0') ^ 1) + '0';
    		}
    		acam.build();
    		printf("%lld
    ", acam.gao());
    	}
    	return 0;
    }
    

    I - Palindromes

    题意:
    找第(k)个回文数。(k)很大。

    思路:
    找规律。

    代码:

    view code
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e6 + 10;
    char s[N];
    
    int main() {
    	int _T; scanf("%d", &_T);
    	while (_T--) {
    		scanf("%s", s + 1);
    		int len = strlen(s + 1);
    		if (len == 1) {
    			printf("%c
    ", s[1] - 1);
    			continue;
    		} 
    		if (s[1] > '1') {
    			s[1]--;
    			printf("%s", s + 1);
    			reverse(s + 1, s + len);
    			s[len] = 0;
    			printf("%s", s + 1);
    		} else if (s[2] == '0') {
    			for (int i = 1; i < len; ++i)
    				s[i] = s[i + 1];
    			s[1] = '9';
    			s[len] = 0; len--;
    			printf("%s", s + 1);
    			reverse(s + 1, s + len);
    			s[len] = 0;
    			printf("%s", s + 1);
    		} else {
    			for (int i = 1; i < len; ++i)
    				s[i] = s[i + 1];
    			s[len] = 0; len--;
    			printf("%s", s + 1);
    			reverse(s + 1, s + len + 1);
    			printf("%s", s + 1);
    		}
    		puts("");
    	}
    	return 0;
    }
    

    J - Rikka with Triangles

    题意:
    给出(n)个点,计算这(n)个点组成的所有锐角三角形的面积和

  • 相关阅读:
    Gradle Android Plugin 中文手册
    WxApiUtil.ts
    通过qrcodejs2和html2canvas把iframe中的内容生成带二维码的海报长图片
    TypeScript--类型声明
    ZJNU 2663
    ZJNU 2652
    etcd学习(7)-etcd中的线性一致性实现
    com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。
    帝国cms显示点击数比后台多1个的解决方法
    RedHat换源
  • 原文地址:https://www.cnblogs.com/Dup4/p/11675241.html
Copyright © 2020-2023  润新知