10.03模拟总结

国庆模拟真愉快,真愉快!这只是个开始,之后我们还有4天连续的国庆模拟呢!而且可能不会像今天这么好改了orz。

来吧,我们来看看这些题。

T1.matrix

这道题考试的时候只想到了60分的暴力维护……就是只开一维差分,另一维直接暴力去修改……,不过这样的复杂度是O(np)的,这个肯定会超时。

然后大致想到了使用二维差分去维护,不过不知道怎么实现。(知道查询是可以用前缀和查询的,但是修改怎么修啊orz)

考试结束后听bin哥一讲就明白了,我们只要在右上和左下+1,左下和右上-1即可,这样的话我们就能保证原数组就是差分数组的前缀和。(只要简单想想就能知道啦)

之后我们再跑一遍,维护一次前缀和。

查询的适合只要用四个矩形互相加减即可,老套路了。

来看一下满分的代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define de putchar('#')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 100005;
const int N = 2005;
const int INF = 1000000009;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

int n,m,p,q,dx,dy,sx,sy,c[N][N];
ll a[N][N];

void build()
{
    rep(q,1,2)
    {
        rep(i,1,n-1)
        rep(j,1,m) a[i+1][j] += a[i][j];
        rep(i,1,n)
        rep(j,1,m-1) a[i][j+1] += a[i][j];
    }
}

int main()
{
    freopen("matrix.in","r",stdin);
    freopen("matrix.out","w",stdout);
    n = read(),m = read(),p = read(),q = read();
    rep(i,1,p)
    {
        dx = read(),dy = read(),sx = read(),sy = read();
        a[dx][dy]++,a[dx][sy+1]--,a[sx+1][dy]--,a[sx+1][sy+1]++;
    }
    build();
    rep(i,1,q)
    {
        dx = read(),dy = read(),sx = read(),sy = read();
        ll g = a[sx][sy] + a[dx-1][dy-1] - a[sx][dy-1] - a[dx-1][sy];
        printf("%lld
",g);
    }
    return 0;
}

T2.card

好吧说实话第一眼我以为这是个组合题,还以为这是个与HNOI2008那道组合很相像的一道题。不过那道题要求维护的是相同的,那个其实特别好计算,而这道题是要求维护的是相邻两个数只和不能等于k,这个就很麻烦。

我一开始想能不能用组合的方法解决……好吧显然不行。这玩意咋组合啊……反正我是想不出来。想了一段时间之后我觉得换个思路,用递推的方式来做。我只考虑了k  < m的情况,设f[i]表示第i个数选择了1~k-1范围之内的数的方案数,g[i]表示第i个数选择了k~m范围内的数的方案树。

于是乎我们有如下转移:

f[i] = f[i-1] * (k-2) + g[i-1] * (k-1) (还有取模)

g[i] = (g[i-1] + f[i-1]) * (m-k+1) (取模)

然后我就这么傻不拉几的交了上去,还得了50pts。

其实我考试结束之前10分钟突然发现这玩意可以用矩阵加速……然后我没时间写了。

哦,对于k > m的情况,一开始我听了ssy神犇的讲述……不过后来觉得好像还是自己想好懂一些。

其实完全可以照着k < m去推的,这次令f[i]表示选取1~k-m-1的情况的方案数,g[i]表示选择k-m~m的情况的方案数。于是得到以下方程:

f[i] = (f[i-1]  + g[i-1])* (k-m-1);

g[i] = f[i-1] * (2m-k+1) + g[i-1] * (2m-k)

然后这玩意也可以用矩阵加速!然后就可以A了!

看一下满分代码。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define de putchar('#')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 2000005;
const ll mod = 1000000007;

ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
    if(ch == '-') op = -1;
    ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
    ans *= 10;
    ans += ch - '0';
    ch = getchar();
    }
    return ans * op;
}

ll m,n,k,f[M],g[M],ans;


struct matrix
{
    ll h[3][3];
    matrix()
    {
        memset(h,0,sizeof(h));
    }
    friend matrix operator * (const matrix &a,const matrix &b)
    {
        matrix c;
        rep(i,1,2)
        rep(j,1,2)
        rep(k,1,2) c.h[i][j] += (a.h[i][k] * b.h[k][j]) % mod,c.h[i][j] %= mod;
        return c;
    }
}a;

matrix mpow(matrix a,ll b)
{
    matrix p;
    rep(i,1,2) p.h[i][i] = 1;
    while(b)
    {
    if(b & 1) p = p * a;
    a = a * a;
    b >>= 1;
    }
    return p;
}

int main()
{
    //freopen("card.in","r",stdin);
    //freopen("card.out","w",stdout);
    m = read(),n = read(),k = read();
    if(k > m)
    {
    a.h[1][1] = (k-m-1) % mod,a.h[1][2] = (k-m-1) % mod;
    a.h[2][1] = (2*m-k+1) % mod,a.h[2][2] = (2*m-k) % mod;
    matrix d = mpow(a,n-1);
    f[1] = (k-m-1) % mod,g[1] = (2*m-k+1) % mod;
    f[2] = (f[1] * d.h[1][1]) % mod + (g[1] * d.h[1][2]) % mod,f[2] %= mod;
    g[2] = (f[1] * d.h[2][1]) % mod + (g[1] * d.h[2][2]) % mod,g[2] %= mod;
    printf("%lld
",(f[2] + g[2]) % mod);
    return 0;
    }
    a.h[1][1] = (k-2) % mod,a.h[1][2] = (k-1) % mod;
    a.h[2][1] = (m-k+1) % mod,a.h[2][2] = (m-k+1) % mod;
    matrix d = mpow(a,n-1);
    f[1] = (k-1) % mod,g[1] = (m-k+1) % mod;
    f[2] = (f[1] * d.h[1][1]) % mod + (g[1] * d.h[1][2]) % mod,f[2] %= mod;
    g[2] = (f[1] * d.h[2][1]) % mod + (g[1] * d.h[2][2]) % mod,g[2] %= mod;
    printf("%lld
",(f[2] + g[2]) % mod);
    return 0;
}

T3.station

这题考试的时候我还是只能想到O(nq)模拟,就是维护一下前缀和然后硬算。

然后莫名其妙的爆零了orz……

后来发现,其实对于每个村庄的距离,他相对于上一个村庄的距离就是加上左边的前缀和,减去右边的前缀和,所以其实它是有一定的单调性的。所以其实它是可以使用树状数组维护的……这倒是学了一招,可以直接维护现在的人数总和,然后其实我们只要在树状数组上二分一个人数总和的一半即可,那个位置一定是最小值。

我们来看一下代码吧。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define de putchar('#')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
const int M = 300005;
const ll INF = 1000000000009;
const ll mod = 998244353;

ll read()
{
    ll ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >='0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

ll naive[M],n,q,a[M],p,b,ans,s,minn,mpos,w = 1;

void add(ll x,ll y)
{
    while(x < M) a[x] += y,x += lowbit(x);
    s += y;
}

int query(ll x)
{
    int t = 0;
    ll cur = 0;
    for(int i = (1 << 17);i;i >>= 1) if(cur + a[t+i] <= x) cur += a[t += i];
    return t + 1;
}

int main()
{
        freopen("station.in","r",stdin);
    freopen("station.out","w",stdout);
    n = read(),p = read();
    rep(i,1,n) b = read(),add(i,b);
    while(p--)
    {
        q = read(),b = read(),add(q,b);
        w *= 19260817ll,w %= mod;
        ans += w * query((s-1) >> 1),ans %= mod;
    }
    printf("%lld
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/captain1/p/9741418.html