暑假第二測
題解:
第一題:
如果A矩形的前綴和MOD K = P, C矩形的前綴和MOD K =P,說明中間的矩形MOD K= 0; 所以枚舉列起點, 終點, 行數, 統計余數出現次數 O(n*M*M);
#include <bits/stdc++.h> const int M = 405, N = 1e6 + 10; using namespace std; #define rt register int a[M][M], sum[M][M], vis[N], lie[M][M], ret[M]; int main() { //freopen("rally.in", "r", stdin);View Code//freopen("rally.out", "w", stdout); int n, m, k; long long cnt = 0; scanf("%d%d%d", &n, &m, &k); for(rt int i = 1; i <= n; i++) for(rt int j = 1; j <= m; j++){ scanf("%d", &a[i][j]); sum[i][j] = (sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j]) % k; lie[i][j] = lie[i][j-1] + a[i][j]; } for(int i = 1; i <= m; i++) for(int j = i; j <= m; j++){ for(int p = 1; p <= n; p++){ ret[p] = (ret[p-1] + lie[p][j] - lie[p][i-1]) % k; vis[ret[p]]= 0; } for(int p = 1; p <= n; p++){ if(ret[p] == 0) cnt++; cnt += vis[ret[p]]; vis[ret[p]]++; } } printf("%lld", cnt); return 0; }
第二題:
45 分做法 k == 1 dp
dp[u][0] control by son dp[u][1] control by oneself dp[u][2] control by father
dp[u][0] = sum(min(dp[v][0], dp[v][1]) (but there must be at least one dp[v][1])
dp[u][1] = sum(min(dp[v][0/1/2])
dp[u][2] = sum(min(dp[v][0],dp[v][1]))
dp:
void dfs(int u, int f){ fa[u] = f; int delta = inf, sum = 0, child = 0; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; child++; dfs(v, u); sum += min(dp[v][1], dp[v][0]); delta = min(delta, max(dp[v][0] - dp[v][1], 0)); if(dp[v][0] < dp[v][1]) pp[u] = 1; dp[u][0] += min(dp[v][1], min(dp[v][0], dp[v][2])); } if(!child){ dp[u][1] = inf; dp[u][0] = 1; return ; } dp[u][0]++; dp[u][1] = dp[u][2] = sum; if(!pp[u]) dp[u][1] += delta; }View Code
AC code
#include <bits/stdc++.h> const int M = 1e5 +10; #define inf 1e8 using namespace std; int tot, n, k, fa[M], t, dep[M], h[M]; bool vis[M]; struct edge{int v,nxt;}G[M<<1]; void add(int u, int v){G[++tot].nxt = h[u]; h[u] = tot; G[tot].v = v; } void dfs(int u, int f){ dep[u] = dep[f] + 1; fa[u] = f; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; dfs(v, u); } } void dst(int u, int dd, int f){ vis[u] = 1; for(int i = h[u]; i; i = G[i].nxt){ int v = G[i].v; if(v == f)continue; if(dd < k)dst(v, dd+1, u); } } struct Node{ int id, dep; bool operator < (const Node& a)const{ return a.dep > dep; } }; priority_queue <Node> Q; int main() { int cnt = 0; scanf("%d%d%d", &n, &k, &t); for(int i = 1; i < n; i++){ int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); } dfs(1, 0); for(int i = 1; i <= n; i++) Q.push((Node){i, dep[i]}); for(int i = 1; i <= n; i++){ int aa = Q.top().id; Q.pop(); if(vis[aa])continue; int tt = 0; while(tt < k){ if(!fa[aa])break; aa = fa[aa]; tt++; } dst(aa, 0, aa); //printf("%d ", aa); cnt++; } printf("%d\n", cnt); return 0; }View Code
第三題:首先我們發現對於連續的一段燈泡去按它的開關極其費時,因此我們需要一種O(1)復雜度的方法進行開關的操作。
對於序列加減我們可以用差分數組, 對於異或也可以差分;
令差分數組 b[i] = a[i]^a[i+1] 例:
a 1 0 0 0 1
b 1 1 0 0 1 1
將1——5翻轉, 則修改B[0] 和 b[5] 就可以了
b 0 1 0 0 1 0
a 0 1 1 1 0
b逆推的a如上; 一個1 會在B中產生至多兩個1, 故最多2k個1.,那麽原問題被我們玄學的轉化為了對於給定的0 1數列,每次對於按要求的某兩個數進行取反,問最少次可以使數列全部變為1.
這裏有必要提一下原問題麻煩的地方,即對於答案無用的燈泡(亮著的)太多,這樣會導致進行大量的無效操作。所以我們只對有1的地方操作,那麽他們轉化的狀態我們就達到某狀態的最小操作次數可以預處理出來,最後答案為全0的狀態; 這個可以用SPFA跑, 然後由於只有2k個1,我們就可以狀壓了;
#include<bits/stdc++.h> using namespace std; const int N = 1<<18,M = 40005, inf = 1e8; int a[M], ans, b[M], cnt; int n, k, m, dis[20][M]; int dp[N]; typedef pair<int, int> pii; pii p[20]; void bfs(pii st){ queue <int> q; for(int i = 0; i <= n; i++) dis[st.first][i] = inf; q.push(st.second); dis[st.first][st.second] = 0; while(!q.empty()){ int u = q.front(); q.pop(); for(int i = 1; i <= m; i++){ if(u + b[i] <= n && dis[st.first][u] + 1 < dis[st.first][u + b[i]]){ dis[st.first][u + b[i]] = dis[st.first][u] + 1; q.push(u + b[i]); } if(u - b[i] >= 0 && dis[st.first][u] + 1 < dis[st.first][u - b[i]]){ dis[st.first][u - b[i]] = dis[st.first][u] + 1; q.push(u - b[i]); } } } } int solve(int sta){ //printf("%d\n", sta); if(dp[sta] != -1)return dp[sta]; if(sta == 0)return 0; dp[sta] = inf; int fi = 0; while(!((1<<fi)&sta))fi++; for(int i = fi+1; i < 2*k; i++) if( (1<<i) & sta ) dp[sta] = min(dp[sta], dis[fi][p[i].second] + solve(sta^(1<<fi)^(1<<i))); //printf("%d %d\n", sta, dp[sta]); return dp[sta]; } int main(){ //freopen("starlit.in","r",stdin); //freopen("starlit.out","w",stdout); scanf("%d%d%d", &n, &k, &m); for(int i = 1; i <= k; i++){ int v; scanf("%d", &v); a[v] = 1; } for(int i = 1; i <= m; i++)scanf("%d", &b[i]); for(int i = 0; i <= n; i++) if(a[i] != a[i+1]) p[cnt] = pii(cnt++, i); for(int i = 0; i < cnt; i++) bfs(p[i]); memset(dp, -1, sizeof(dp)); int ans = solve((1<<cnt)-1); printf("%d",ans); }View Code
暑假第二測