暑假第二测

Posted edsheeran

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了暑假第二测相关的知识,希望对你有一定的参考价值。

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

技术分享图片

题解:

第一题:技术分享图片

技术分享图片

如果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
", 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
", 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
", 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

 





以上是关于暑假第二测的主要内容,如果未能解决你的问题,请参考以下文章

ZR七连测第二测总结

开学第二测

18寒假第二测

9.5ZR提高组十连测第二测

BZOJ NOI十连测 第二测 T2

5.31联合第二测