平时十六测

---恢复内容开始---

题解;

第一题:简单状压 dp[i][s]表示处理到第i位,他的上一位(1),当前位置(2),下一位的状态为s的方案数(4);

我每次保证i这个s状态合法,然后往下讨论i+1的情况;

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int M = 1e6 + 5;
int dp[M][(1<<3)+1];
inline int go(char a){
    switch(a){
        case '?': return 3;
        case '2': return 2;
        case '1': return 1;
        case '0': return 0;
        case '*': return 4;
    }
}
inline int up(int &x, int b){
    x += b;
    if(x >= mod) x -= mod;
}
char ss[M];
int main(){
    freopen("mine.in","r",stdin);
    freopen("mine.out","w",stdout);
    scanf("%s", ss);
    int len = strlen(ss);
    int t = go(ss[0]);
    if(t == 0) dp[0][0] = 1;
    else if(t == 1) dp[0][4] = 1;
    else if(t == 4) dp[0][2] = 1, dp[0][6] = 1;
    else if(t == 3) dp[0][0] = dp[0][2] = dp[0][4] = dp[0][6] = 1;
    for(int i = 0; i < len - 1; i++)
        for(int s = 0; s < (1<<3); s++){
            if(dp[i][s]){
                int t = go(ss[i+1]);
                int p1 = s&2 ? 1 : 0, p2 = s&4 ? 1 : 0;
                if(t == 0){
                    if(!p1 && !p2) up(dp[i+1][s>>1], dp[i][s]);
                }
                else if(t == 1){
                    if(p1 && !p2) up(dp[i+1][s>>1], dp[i][s]);
                    if(!p1 && !p2) up(dp[i+1][s>>1|4], dp[i][s]);
                }
                else if(t == 2){
                    if(p1 && !p2) up(dp[i+1][s>>1|4], dp[i][s]);
                }
                else if(t == 3){
                    up(dp[i+1][s>>1], dp[i][s]);
                    up(dp[i+1][s>>1|4], dp[i][s]);
                }
                else if(t == 4){
                    if(p2) {
                        up(dp[i+1][s>>1], dp[i][s]);
                        up(dp[i+1][s>>1|4], dp[i][s]);
                    }
                }
                //printf("%d %d %d\n", i, s, dp[i][s]);
            }
        }
    int ans = 0;
    t = go(ss[len-1]);
    if(t == 0) up(ans, dp[len-1][0]);
    else if(t == 1) up(ans, dp[len-1][1]);
    else if(t == 4) up(ans, dp[len-1][3]), up(ans, dp[len-1][2]);
    else if(t == 3) up(ans, dp[len-1][0]), up(ans, dp[len-1][1]), up(ans, dp[len-1][2]), up(ans, dp[len-1][3]);
    printf("%d\n", ans);
}
View Code

第二题:

这道题可以爆搜+剪枝过;对于一个坑,

1.他流出去变成0;

2.周围太高,他被累积在里面;

1可以暴力bfs+记忆化 O(N*N);

2.比他低的我就流出去覆盖,比他高的就在七中找最小值,还是加一个记忆化;

以下就是zyy大佬的优美搜索:

#include<bits/stdc++.h>

inline int read() {
    char c;
    int res = 0, f = 1;
    while((c = getchar()) < '0' || c > '9')    if(c == '-')    f = -f;
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = getchar();
    }
    return res * f;
}

const int MAXN = 300 + 5;
const int INF = 0x7fffffff;

int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
int n, m;
int h[MAXN][MAXN], ans[MAXN][MAXN], vis1[MAXN][MAXN], vis2[MAXN][MAXN];
int dft, tmp;

bool inMap(int x, int y) {
    return x >= 0 && y >= 0 && x <= n + 1 && y <= m + 1;
}

void dfr(int x, int y) {
    if(vis1[x][y])    return;
    vis1[x][y] = true;
    for(int k = 0; k < 4; k++) {
        int xx = x + dx[k], yy = y + dy[k];
        if(!inMap(xx, yy)) {
            ans[x][y] = 0;
        } else if(h[xx][yy] <= h[x][y]){
            dfr(xx, yy);
            if(ans[xx][yy] == 0)    ans[x][y] = 0;
        }
    }
}

bool dfs(int x, int y, int u) {
    if(vis2[x][y] == dft)    return true;
    if(h[x][y] > u) {
        tmp = std::min(tmp, h[x][y] + ans[x][y]);
        return true;
    }
    if(ans[x][y] == 0)
        return false;
    vis2[x][y] = dft;
    for(int k = 0; k < 4; k++) {
        int xx = x + dx[k], yy = y + dy[k];
        if(!dfs(xx, yy, u))    return false;
    }
    return true;
}

struct Point {
    int x, y, height;

    bool operator < (const Point &t) const {
        return height > t.height;
    }
} points[MAXN * MAXN];

int main() {
    freopen("water.in", "r", stdin);
    freopen("water.out", "w", stdout);

    n = read(), m = read();
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            h[i][j] = read();
            points[(i - 1) * m + j] = (Point) {i, j, h[i][j]};
        }
    std::sort(points + 1, points + 1 + n * m);
    memset(ans, -1, sizeof(ans));
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            dfr(i, j);
    for(int i = 1; i <= n * m; i++) {
        dft++;
        tmp = INF;
        if(!dfs(points[i].x, points[i].y, points[i].height))
            ans[points[i].x][points[i].y] = 0;
        else
            ans[points[i].x][points[i].y] = tmp - points[i].height;
    }    
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++)
            printf("%d ", ans[i][j]);
        printf("\n");
    }
}
View Code

正解:最小生成树;

对于可以走出去(矩形外)的,这个路径上的最大值是可以确定的,我们考虑在里面的点和外界连通,那么他第一次通向外界的边就是当前最小生成树加上的这条边,而且这条边是通向外界的最小路径上的最大边(思考),所以在里面封闭的一团的答案就是这条边的权值;

#include<bits/stdc++.h>
using namespace std;

int zl[4][2] = {{0,1}, {1,0}, {-1,0}, {0,-1}};
int ans[305][305], mp[305][305], f[305*305], n, m, pd[305*305], h[320*320], cnt;
struct Edge{int u, v, w;}g[900000];
struct edge{int v, nxt;}G[900000];
bool cmp(Edge A, Edge B){return A.w < B.w;}
void add(int u, int v){
    G[++cnt].v = v, G[cnt].nxt = h[u], h[u] = cnt;
}
inline int id(int x, int y){
    return x*(m+2) + y;
}
int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}
void dfs(int x, int w){
    ans[x/(m+2)][x%(m+2)] = w;
    for(int i = h[x]; i; i = G[i].nxt){
        int v = G[i].v;
        dfs(v, w);
    }
}

int main(){
    freopen("water.in","r",stdin);
    freopen("water.out","w",stdout);
    int tot = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            scanf("%d", &mp[i][j]);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            for(int k = 0; k < 4; k++){
                int x = i + zl[k][0], y = j + zl[k][1];
                g[++tot] = (Edge) {id(i, j), id(x, y), max(mp[i][j], mp[x][y])};
            }
    /*for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)printf("%d ",id(i,j));puts("");
    }*/
    for(int i = 0; i <= id(n+1, m+1); i++) f[i] = i;
    for(int i = 0; i <= n+1; i++){
        pd[id(i, 0)] = pd[id(i, m+1)] = 1;    
    } 
    for(int i = 0; i <= m+1; i++){
        pd[id(0, i)] = pd[id(n+1, i)] = 1;
    } 
    sort(g + 1, g + 1 + tot, cmp);
    for(int i = 1; i <= tot; i++){
        int fu = find(g[i].u), fv = find(g[i].v);
        if(fu == fv) continue;
        if(pd[fu] && !pd[fv]){
            dfs(fv, g[i].w);
        }
        else if(pd[fv] && !pd[fu]){
            dfs(fu, g[i].w);
        }
        f[fv] = fu;
        pd[fu] |= pd[fv];
        add(fu, fv);
        //printf("%d %d %d %d %d %d\n", fu/(m+2), fu%(m+2), fv/(m+2), fv%(m+2), pd[fu], pd[fv]);
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++)printf("%d ", ans[i][j] - mp[i][j]);
        puts("");
    }
        
    
}
View Code

第三题:(为什么我觉得这道题考过,而且当时就是yl大佬讲的?我可以失去了记忆)

求不互质的显然比求互质的更困难,所以我们求互质的;

互质联想到什么,拆分质因数!

容斥:我们减去gcd都是2倍数的贡献,减去是3倍数的贡献,加上是6倍数的贡献……

发现有以下性质:

1.容斥原理对每个数的质因数拆分,奇减偶加;

2.如果一个质因数出现了两次,如2*3*5*5,筛2,3,5的时候就会把后面的贡献处理掉,这个多余的5没用,他整个数对后面也没有用;

所以质因数出现两次,贡献为0;

3.1的贡献是1;

这个恰好符合莫比乌斯函数的性质:https://www.cnblogs.com/peng-ym/p/8647856.html

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int M = 500005;
bool in[M], vis[M];
int a[M], mul[M], tot, prime[M], cnt[M];
void init(){
    mul[1] = 1;
    for(int i = 2; i < M; i++){
        if(!vis[i]) prime[++tot] = i, mul[i] = -1;
        for(int j = 1; j <= tot && i * prime[j] < M; j++){
            int m = i * prime[j];
            vis[m] = 1;
            if(i % prime[j] == 0)break;
            mul[m] = -mul[i];
        }
    }
}
ll ans = 0;

void add(int x, int opt){
    for(int i = 1; i * i <= x; i++){
        if(x % i)continue;
        ans -= ( (ll) cnt[i] * (cnt[i] - 1) / 2 * mul[i] ) ;
        cnt[i] += opt;
        ans += ( (ll) cnt[i] * (cnt[i] - 1) / 2 * mul[i] );
        if(x / i == i)continue;
        ans -= ( (ll) cnt[x/i] * (cnt[x/i] - 1) / 2 * mul[x/i] );
        cnt[x/i] += opt;
        ans += ( (ll) cnt[x/i] * (cnt[x/i] - 1) / 2 * mul[x/i] );  
    }
}
int read(){
    int x = 0; int f = 1; char c = getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
    return x*=f;
}
int main(){
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    init();
    int n = read(), m = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    while(m--){
        int u = read();
        if(in[u])add(a[u], -1);
        else add(a[u], 1);
        in[u] ^= 1;
        printf("%lld\n", ans);
    }
}
View Code

---恢复内容结束---

猜你喜欢

转载自www.cnblogs.com/EdSheeran/p/9858322.html