1. 程式人生 > >暑假第二測

暑假第二測

++ bfs span dde there 前綴和 control ems con

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

技術分享圖片

題解:

第一題:技術分享圖片

技術分享圖片

如果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);
    
//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; }
View Code

第二題:

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

暑假第二測