The 2014 ACM-ICPC Asia Anshan Regional Contest
A题 - Twelve Months
没法补
B题 - Chat
大模拟,队友已补
C题 - Coprime
三角形同色模型。假设有n个元素,每两个元素间都存在两种不同的关系A和B。现在要求找出所有三元组,使得它们两两间均为关系A或关系B,总共有多少种可能。
解决:反过来想,我们可以先找出不符合的关系数,那么问题就变为找关系即存在A又存在B的元组。我们先遍历所有元素,假如和该元素符合A关系的元素数目为$a$,符合B关系的元素数目为$b$,那么这个元素对答案有的贡献。对于每个符合的关系$(a_{i}, a_{j}, a_{k})$如果被算进了贡献,我们把每个关系看成一条边,那么有且只有两条边是相同的,这两条边的公共点不会对答案做出贡献,但另外两个点必会对答案做出贡献,所以上面我们提到的关系$(a_{i}, a_{j}, a_{k})$其实被两个元素用到,所以最后的贡献我们要除2。至于其他细节就很套路了。
代码:
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; const int M = 1e3 + 5; const int N = 1e5 + 5; typedef long long ll; int prime[N]; bool vis[N]; int cn = 0; void init() { for(int i = 2; i < M; ++ i) { if(!vis[i]) prime[cn ++] = i; for(int j = 0; j < cn && i * prime[j] < M; ++ j) { vis[i * prime[j]] = 1; if(i % prime[j] == 0) break; } } } int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); } int num[N]; int fac[N][10]; int facn[N]; int a[N]; void getfac(int p, int x) { facn[p] = 0; for(int i = 0; i < cn; ++ i) { if(x < prime[i]) break; if(x % prime[i] == 0) { fac[p][facn[p] ++] = prime[i]; while(x % prime[i] == 0) x /= prime[i]; } if(x == 1) break; } if(x != 1) fac[p][facn[p] ++] = x; return ; } void getnum() { for(int i = 2; i < N; ++ i) for(int j = i + i; j < N; j += i) num[i] += num[j]; } int RC(int p) { int res = 0; int lim = (1<<facn[p]); for(int i = 1; i < lim; ++ i) { int mul = 1, p1 = 0; for(int k = 0; k < facn[p]; ++ k) { if(i & (1<<k)) { mul *= fac[p][k]; ++ p1; } } if(p1&1) res += num[mul]-1; else res -= num[mul]-1; } return res; } int main() { //clock_t t = clock(); int T; init(); scanf("%d", &T); while(T --) { int n, maxs = 1; scanf("%d", &n); memset(num, 0, sizeof(num)); for(int i = 0; i < n; ++ i) { scanf("%d", a + i); num[a[i]] ++; getfac(i, a[i]); //maxs = max(maxs, a[i]); } getnum(); ll pr = 0; for(int i = 0; i < n; ++ i) { int res = RC(i); pr += 1ll * res * (n - res - 1); } pr /= 2; printf("%I64d\n", 1ll*n*(n-1)*(n-2)/6 - pr); } //cout << clock()-t << "MS" << endl; return 0; }
D题 - Galaxy
带权中位数?因为权值都为1,我们直接一个区间一个区间地枚举即可,$O(1)$转移没问题。
E题 - Hatsune Miku
简单dp题,dp[p][j]表示第p个位置为j时最优解是什么,其可以由dp[p-1][k]转移过来。
F题 - Random Inversion Machine
不会
G题 - Memory
貌似是最小割,但并不会。
H题 - NAND
暴搜,写个dfs把所有结果记录下来就行了,但dfs比较恶心,要剪纸才行。
I题 - Osu!
签到题,直接模拟即可。
J题 - Square
状压dp。
解决:比赛时知道了这题要状压dp,但发现一个问题:对于每一行,如果我们记录当前行摆放情况和前k行每一列的摆放情况,那么状态会特别大。我们换种思路,把题意做一步退化,使其变为:计算最大子矩阵边长小于k的情况数。那么我们在计算合法状态时只需考虑连续k个方格是否每列均大于k,这样每行只需记录$(n-k+1)*k$个状态。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 6 const int N = 8; 7 const int M = 2000 + 5; 8 const ll MOD = 1e9 + 7; 9 10 char mp[N+5][N+5]; 11 12 int pw[N+1][N+1]; 13 ll dp[N+1][M]; 14 int bar[N+1]; 15 16 void init() 17 { 18 for(int i = 1; i <= N; ++ i) { 19 pw[i][0] = 1; 20 for(int j = 1; j <= N; ++ j) 21 pw[i][j] = pw[i][j-1] * i; 22 } 23 } 24 25 int getp(int s, int b, int i) 26 { 27 return s / pw[b][i] % b; 28 } 29 30 int putp(int s, int b, int i, int val) 31 { 32 int tmp = s % pw[b][i]; 33 return (s / pw[b][i+1] * b + val) * pw[b][i] + tmp; 34 } 35 36 int main() 37 { 38 //freopen("in", "r", stdin); 39 int T; 40 init(); 41 scanf("%d", &T); 42 while(T --) { 43 int n, ba = 1; 44 scanf("%d", &n); 45 for(int i = 1; i <= n; ++ i) { 46 scanf("%s", mp[i]); 47 bar[i] = 0; 48 for(int j = 0; j < n; ++ j) 49 if(mp[i][j] == '*') { 50 ba = 0; bar[i] |= (1 << j); 51 } 52 } 53 printf("1\n"); 54 ll pre = 1, ans = 0; 55 for(int d = 2; d <= n; ++ d) { 56 int lim = pw[d][n-d+1]; 57 memset(dp, 0, sizeof(dp)); 58 dp[0][0] = 1; 59 for(int i = 0; i < n; ++ i) { 60 for(int si = 0; si < lim; ++ si) { 61 for(int s = 0; s < (1<<n); ++ s) { 62 if(s & bar[i+1]) continue; 63 int nsta = 0, ok = 1; 64 for(int j = 0; j < n-d+1; ++ j) { 65 int np = getp(si, d, j); 66 int tmp = (1<<j+d)-(1<<j); 67 if((s & tmp) == tmp) np ++; 68 else np = 0; 69 if(np == d) { ok = 0; break; } 70 nsta = putp(nsta, d, j, np); 71 } 72 if(!ok) continue; 73 dp[i+1][nsta] = (dp[i+1][nsta] + dp[i][si]) % MOD; 74 } 75 } 76 } 77 ans = 0; 78 for(int si = 0; si < lim; ++ si) 79 ans = (ans + dp[n][si]) % MOD; 80 printf("%lld\n", (ans - pre + MOD) % MOD); 81 pre = ans; 82 } 83 printf("%d\n", ba); 84 } 85 return 0; 86 }
K题 - Colorful Toy
几何+Ponya定理。
现在暂时不太会。留个坑。
L题 - Trie
不会。