八一特别行动

护身旁,战远方,有我啊,一生骄傲为我站立的地方,我的样子就是中国的模样。

A. 南

解释一下g[i]的转移:+f[i]其实就是+f[i]*1把后面计算过的每个贡献都+1,就像这样

    1     每次只计算了后面的,从后往前转移,新加的是1,原来的就让出位置,而再加一个1是当前多

  12     加的步数。

123

(⊙o⊙)…总之就是……(⊙o⊙)…(⊙o⊙)…感性理解……

https://blog.csdn.net/u011815404/article/details/88973189

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;

double n, f[maxn], g[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    scanf("%lf", &n);
    for(int i=n-1; i>=0; i--)
    {
        f[i] = f[i+1] + n/(n-i)*1.0;
    }
    for(int i=n-1; i>=0; i--)
    {
        g[i] = i/(n-i)*1.0*f[i]+g[i+1]+f[i+1]+n/(n-i)*1.0;
    }
    printf("%.2lf\n", g[0]);
    
    return 0;
}
View Code

B. 昌

暴力就是,枚举每一种叶子の可能的排列,直接填进去向上合并。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 2;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;

vector<int> son[maxn], leaf;
int b[maxn], w[maxn], ans, sz, n;
bool tag[maxn], v[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int get_ans(int u)
{
    if(!son[u].size())
    {
        return w[u];
    }
    int res = 0;
    if(!tag[u]) res = INF;
    for(int i=0; i<son[u].size(); i++)
    {
        int v = son[u][i];
        if(tag[u]) res = max(res, get_ans(v));
        else res = min(res, get_ans(v));
    }
    return res;
}

void check()
{
    for(int i=0; i<sz; i++)
    {
        int u = leaf[i];
        w[u] = b[i+1];
    }
    ans = max(ans, get_ans(1));
}

void dfs(int a, int n)
{
    if(a > n)
    {
        check(); return;
    }
    for(int i=1; i<=n; i++)
    {
        if(v[i]) continue;
        v[i] = 1;
        b[a] = i;
        dfs(a+1, n);
        v[i] = 0;
    }
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++)
    {
        tag[i] = read();
    }
    for(int i=2; i<=n; i++)
    {
        int x = read();
        son[x].push_back(i);
    }
    for(int i=2; i<=n; i++)
    {
        if(!son[i].size())
        {
            leaf.push_back(i);
        }
    }
    sz = leaf.size();
    dfs(1, sz);
    printf("%d\n", ans);
    
    return 0;
}
20%

假设最后根节点的权值>=x,那么min操作的话,儿子的权值必须都>=x,max操作的话,儿子的权值必须有一个>=x,假设要满足根节点的权值>=x需要有cnt(也就是下文的f[1])个叶子>=x,无论x是多少cnt唯一确定,x可以是小于(设f[i]是以i为根的子树内至少有多少个叶子需要大于某一个值)f[i]的集合里的任意一个数,它最大时有x+f[1]-1<=k,也就是x<=k-f[1]+1,这就可以通过求出f[1]来得到x。其实不止有cnt,f数组的所有值都不会随x变化而变化,所以x是不是相同不重要,它一定可以向上合并。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 2;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;

vector<int> son[maxn], leaf;
int f[maxn], sz, n;
bool tag[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int dfs(int u)
{
    if(!son[u].size())
    {
        return 1;
    }
    if(tag[u]) f[u] = INF;
    for(int i=0; i<son[u].size(); i++)
    {
        int v = son[u][i];
        if(tag[u])
        {
            f[u] = min(f[u], dfs(v));
        }
        else f[u] += dfs(v);
    }
    return f[u];
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++)
    {
        tag[i] = read();
    }
    for(int i=2; i<=n; i++)
    {
        int x = read();
        son[x].push_back(i);
    }
    for(int i=2; i<=n; i++)
    {
        if(!son[i].size())
        {
            leaf.push_back(i);
        }
    }
    sz = leaf.size();
    printf("%d\n", sz-dfs(1)+1);
    
    return 0;
}
View Code
const int N=1000000+100;ll mod=998244353;
int n,op[300000+10],fa[300000+10],dp[300000+10];
bool isn[300000+10],ok[300000+10];
int main()
{
   //freopen("shuju.in","r",stdin);
   // freopen("dfs.txt","w",stdout);
    n=re();int ki=0;
    _f(i,1,n)op[i]=re();
    _f(i,2,n)fa[i]=re(),isn[fa[i]]=1;//不是儿子
    _f(i,1,n)if(!isn[i])dp[i]=1,++ki;
    f_(i,n,1)
    {
        if(op[fa[i]])
        {
            if(ok[fa[i]])dp[fa[i]]=min(dp[fa[i]],dp[i]);
            else dp[fa[i]]=dp[i],ok[fa[i]]=1;
        }
        else
        {
            dp[fa[i]]+=dp[i];
        }
    }
    //_f(i,1,n)chu("dp[%d]:%d\n",i,dp[i]);
    chu("%d",ki-dp[1]+1);
    return 0;
}
/*
dp[rt]=cnt:rt节点要>=某个值,至少的叶子节点需要>=这个值的数量
如果rt取max,那么只要有一个就行
dp[rt]=min(dp[son])
如果rt取min,必须所有叶子节点都要>=这个值
dp[rt]=sigma(dp[son])
非递归版本from caorong

 

C. 起

赛时思路小心被误导:不管合法的布料有多大,拼接的部分都相同是那四个字母,所以我预处理出了每个B做右下角,W做左下角……的最大正方形边长,最后询问时先找到拼接部分,把边界和四个正方形最大边长去个min,就是1/4正方形的边长,emmm答案就有了,过样例过得挺顺,交上去就爆0,不过预处理正方形什么的好像和正解有相似之处,大概是我写丑了……

还有一个傻乎乎的操作是n=1直接输出一个0结束程序……n=0不代表q=0呀……这就是送分不要。

 

正解告诉我们,原来正方形的大小可以通过递推得到,而不一定要用二维树状数组求和+判断……

V是符号反了,应该开口朝下……前缀和什么只是个说法,其实说成后缀和似乎更形象……最重要的是注意细节,check函数里要减去的只要有一点点边界超出了范围都不行的所有情况,val*2是长度,u-val*2+1是边界,因为长度恰好为val*2的属于合法情况,所以u-val*2+1是合法的边界,把合法区间的左右边界求和要减掉不合法的,也就是u-val*2+1+1,就是我一直以为我多加的那条宽为1的线它属于合法的范围……emmm想这个想了好久(注释掉的是没用前缀和+二分优化的TLE40代码and调试)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 502;
const ll mod = 1e9 + 7;
const int INF = 2147483647;
const int lim = 1e4 + 1;

int n, m, q, g[maxn][maxn][5], ans;
int f[maxn][maxn][maxn];
char s[maxn][maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
/*
int check(int a, int b, int u, int r)
{
    int ans = 0;
    for(int i=a; i<=u; i++)
    {
        for(int j=b; j<=r; j++)
        {
            for(int k=1; (i+2*k-1<=u && j+2*k-1<=r); k++)
            {
                if(f[i][j][k])
                {
                    ans = max(ans, k);
                }
            }
        }
    }
    return (ans+ans)*(ans+ans);
}
*/
bool check(int val, int a, int b, int u, int w)
{
    //printf("f[2][1][1] = %d\n", f[2][1][1]);
    //printf("f[2][3][1] = %d\n", f[2][3][1]);
    //printf("a = %d w+1 = %d\n", a, w+1);
    //printf("%d %d %d %d\n", f[a][b][val], f[u+1][b][val], f[a][w+1][val], f[u+1][w+1][val]);
    if(!val) return 1;
    if(f[a][b][val]-f[u+1-val*2+1][b][val]-f[a][w+1-val*2+1][val]+f[u+1-val*2+1][w+1-val*2+1][val]) return 1;
    return 0;
}

int main()
{
    n = read(); m = read(); q = read();
    for(int i=1; i<=n; i++)
    {
        scanf("%s", s[i]+1);
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            if(s[i][j] == 'B')
            {
                g[i][j][1] = min(g[i-1][j][1], min(g[i][j-1][1], g[i-1][j-1][1]))+1;
            }
            else if(s[i][j] == 'W')
            {
                g[i][j][2] = min(g[i-1][j][2], min(g[i][j-1][2], g[i-1][j-1][2]))+1;
            }
            else if(s[i][j] == 'P')
            {
                g[i][j][3] = min(g[i-1][j][3], min(g[i][j-1][3], g[i-1][j-1][3]))+1;
            }
            else 
            {
                g[i][j][4] = min(g[i-1][j][4], min(g[i][j-1][4], g[i-1][j-1][4]))+1;
            }
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            for(int k=1; (i+2*k-1<=n&&j+2*k-1<=m); k++)
            {
                if(g[i+k-1][j+k-1][1]>=k && g[i+k-1][j+2*k-1][2]==k && g[i+2*k-1][j+k-1][3]==k && g[i+2*k-1][j+2*k-1][4]==k)
                {
                    f[i][j][k] = 1;
                }
            }
        }
    }
    //printf("f[2][1][1] = %d\n", f[2][1][1]);
    for(int i=n; i>=1; i--)
    {
        for(int j=m; j>=1; j--)
        {
            for(int k=1; (i+2*k-1<=n&&j+2*k-1<=m); k++)
            {
                f[i][j][k] += f[i][j+1][k]+f[i+1][j][k]-f[i+1][j+1][k];
            }
        }
    }
    //printf("cmp main: %d %d\n", f[2][1][1], f[2][3][1]);
    while(q--)
    {
        int a = read(), b = read(), u = read(), w = read();
        int l = 0, r = min(u-a+1, w-b+1)/2;
        //printf("r = %d\n", r);
        while(l < r)
        {
            int mid = (l + r + 1) >> 1;
            //printf("mid = %d\n", mid);
            if(check(mid, a, b, u, w)) l = mid;
            else r = mid-1;
        }
        ans = (l+l)*(l+l);
        printf("%d\n", ans);
    }
    
    return 0;
}
View Code

 

D. 义

由于sqrt(n)之后的物品是不可能被取完的,所以前后可以分开dp,f[i][j](前)和g[i][j](后)中i的含义不一样,f[i][j]中的i指的是取完第i种物品,g[i][j]中的i指的是拿了i件sqrt(n)+1~n中的物品,j都是指已经被占用的空间。所以转移方程就好理解了,g的第二条方程记得i+1。

由于定义的差别,也就可以理解为什么记录答案时,f一直用n,因为他必须讨论结束时的状态,而f的前一项却需要循环,因为从sqrt(n)+1~n中取几个都可以,数组中的没一个数都代表了最后的时间(f中被滚动数组压掉的部分是没到最后时间的过程)。

再解释一下代码中的g[1][0]=1,根据定义这显然是不合法的,所以它不能出现在dp转移之前,可是后来为什么会需要这种不合法的东西,是因为用前sqrt(n)种物品把背包填满也是可行解,但是把它和本来没有值的g[...][0]相乘就变成了0,那为什么不把g[...][0]全附上1,因为f[n][n]作为可行解搭配sqrt(n)之后的不选只能算是一种情况,多加就成了重复。

所以说g[1][0]=1只是为了记录答案的方便并没有实际意义,不过或许在最开始把f[n][n]直接加到ans其实更方便还省了一层循环……

重复一遍,最后一行的g[i][j+sqrt(n)+1]=...的转移应该写成g[i+1][j+sqrt(n)+1]=...

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;
const ll mod = 23333333;
const int INF = 2147483647;
const int lim = 1e4 + 1;

int n, m, f[2][maxn], g[320][maxn], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    n = read(); m = sqrt(n);
    f[0][0] = f[1][0] = 1;
    int p = 0;
    for(int i=1; i<=m; i++)
    {
        p ^= 1;
        for(int j=1; j<=n; j++)
        {
            f[p][j] = f[p^1][j];
            if(j >= i) f[p][j] = (f[p][j]+f[p][j-i])%mod;
            if(j >= i*(i+1))
            {
                f[p][j] = (f[p][j]-f[p^1][j-i*(i+1)]+mod)%mod;
            }
        }
    }
    g[0][0] = 1;
    for(int i=0; i<=m; i++)
    {
        for(int j=0; j<=n; j++)
        {
            if(j+i<=n && i) g[i][j+i] = (g[i][j+i]+g[i][j])%mod;
            if(j+m+1<=n) g[i+1][j+m+1] = (g[i+1][j+m+1]+g[i][j])%mod;
        }
    }
    g[1][0] = 1;
    for(int i=0; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            ans = (ans+1ll*f[p][i]*g[j][n-i]%mod)%mod;
        }
    }
    printf("%d\n", ans);
    
    return 0;
}
感谢eafoo

 

posted @ 2022-08-01 20:36  Catherine_leah  阅读(73)  评论(1编辑  收藏  举报
/* */