1. 程式人生 > >The 2014 ACM-ICPC Asia Anshan Regional Contest

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; }
View Code

 

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 }
View Code

 

K題 - Colorful Toy

幾何+Ponya定理。

現在暫時不太會。留個坑。

 

L題 - Trie

不會。