CSP模拟20

跳火山、赞美太阳、幽邃主教群、整理

A. [POI2014] PAN-Solar Panels

区间 \(\left( l,r \right]\) 中存在 \(n\) 的倍数的充要条件是 \(\left\lfloor \frac{r}{n}\right\rfloor > \left\lfloor \frac{l}{n}\right\rfloor\)

证明:记有整数 \(k\) 满足 \(k \times n \in \left( l,r \right]\)

那么有 $$\displaystyle l < k \times n \leqslant r \Longleftrightarrow \dfrac{l}{n} < k \leqslant \dfrac{r}{n} \Longleftrightarrow \left\lfloor \frac{l}{n}\right\rfloor < \left\lfloor \frac{r}{n}\right\rfloor$$
证毕。

\(\gcd(x,y)=k\),我们可以枚举 \(k\),因为 \(a \leqslant x \leqslant b\),所以我们可以枚举 \(k\)

但暴力枚举 \(k\) 肯定是会超时,那我们就用整除分块优化。

Code

#include <bits/stdc++.h>

using namespace std;

int n;
int a,b,c,d;
int last,ans;

int main() {
#ifdef ONLINE_JUDGE == 1
    freopen("melina.in","r",stdin);
    freopen("melina.out","w",stdout);
#endif
    cin >> n;
    for(int t = 1;t <= n; t++) {
        cin >> a >> b >> c >> d;

        for(int i = 1;i <= b && i <= d; i = last + 1) {
            last = min(d / (d / i),b / (b / i));
            // 整除分块的右端点,实际是范围内的最大值 
            if(b / last > (a - 1) / last && d / last > (c - 1) / last)
                ans = last;// 利用性质 
        }

        cout << ans << "\n";
    }
#ifdef ONLINE_JUDGE == 1
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

B. [POI2010] CHO-Hamsters]

在 AC 自动机上跑 DP,再矩阵快速幂优化。

直接咕!

C. 「JOISC 2016 Day 2」雇佣计划

假设我们有一条线从下向上移动,可以理解为 \(b_i\) 的递增。

在这个过程中,会有数小于 \(b_i\),导致答案数发生变化。我们将小于 \(b_i\) 的数称作不存在。

如下图:

以下的过程不是互相独立的。

如果 \(b_i>3\),由于 \(3\) 的两边都是存在的,答案数会加 \(1\)

如果 \(b_i>4\),由于 \(4\) 的左边存在,右边不存在,所以答案数不变;

如果 \(b_i>5\),由于 \(5\) 的两边都不存在,答案数会减 \(1\)

以上是 \(40 \text{pts}\) 的思路,对正解有一定的启发。


不去考虑连通块的问题,而是去考虑数与数之间的空。

每两个整数之间就可能称为一个谷,显然,一个连通块可以产生两个谷,那么最终答案就是谷的数量除以 \(2\)

我们需要维护有多少个空能够满足成为谷的条件。

考虑和上面 \(40 \text{pts}\) 做法的共通之处,不过也不太一样。什么样的空格可以成为谷?什么样的情况可以对答案产生贡献。

显然,谷的一边的数要小于 \(b_i\),另一边的数要大于等于 \(b_i\),形式化地,我们有:

\[\min(x,y)+1\leq b_i \leq \max(x,y) \]

那我们用什么维护呢?需要在 \(\operatorname{O}(\log n)\) 复杂度内维护上述条件点的数量,还要支持单点修改区间查询,显然,用我们树状数组。

把询问条件差分用树状数组维护,记得离散化。

还有就是 \(0\)\(n+1\) 这两个点,要记录最两边的谷。

#include <bits/stdc++.h>

using namespace std;

const int N = 1005000;

int n,m;
int val[N];

int len,b[N];

int tot = 0;

struct Query{
    int opt;
    int x,y;
}q[N];

int Get(int x) {
    return lower_bound(b + 1,b + len + 1,x) - b;
}

class BIT{
private:
    int sum[N];

    int lowbit(int x) {
            return x & (-x);
    }
public:
    void Update(int x,int y) {
        for(int i = x;i <= 3 * n; i += lowbit(i))
            sum[i] += y;
    }

    int Query(int x) {
        int ans = 0;
        for(int i = x;i;i -= lowbit(i))
            ans += sum[i];
        return ans;
    }
}tree;

void Add(int x,int y) {
    int l = val[x - 1],r = val[x];
    if(l > r)
        swap(l,r);
    
    tree.Update(++l,y);
    tree.Update(r + 1,-y);

    l = val[x];
    r = val[x + 1];

    if(l > r)
        swap(l,r);
    
    tree.Update(++l,y);
    tree.Update(r + 1,-y);
}

int main() {
#ifdef ONLINE_JUDGE == 1
    freopen("darkteam.in","r",stdin);
    freopen("darkteam.out","w",stdout);
#endif
    cin >> n >> m;
    for(int i = 1;i <= n; i++) 
        cin >> val[i];
    
    for(int i = 1;i <= m; i++) {
        cin >> q[i].opt;
        if(q[i].opt == 1) 
            cin >> q[i].x;
        else 
            cin >> q[i].x >> q[i].y;
    }

    for(int i = 1;i <= n; i++) {
        tot ++;
        b[tot] = val[i];
    }

    for(int i = 1;i <= m; i++) {
        tot ++;
        if(q[i].opt == 1)
            b[tot] = q[i].x;
        else
            b[tot] = q[i].y;
	}
			
    sort(b + 1,b + tot + 1);
    len = unique(b + 1,b + tot + 1) - (b + 1);
		
    for(int i = 1;i <= n; i++)
	    val[i] = Get(val[i]);
				
    for(int i = 1;i <= m; i++) {
        if(q[i].opt == 1)
            q[i].x = Get(q[i].x);
        else
            q[i].y = Get(q[i].y);
	}					
			
    for(int i = 1;i <= n + 1; i++) {
        int l = val[i - 1],r = val[i];
	        
        if(l > r)
            swap(l,r);
			
        tree.Update(++l,1);
        tree.Update(++r,-1);
    }
		
    for(int i = 1;i <= m; i++) {
        if(q[i].opt == 1)
            cout << tree.Query(q[i].x) / 2 << "\n";
        else {
            Add(q[i].x,-1);
            val[q[i].x] = q[i].y;
            Add(q[i].x,1);
        }
    }

#ifdef ONLINE_JUDGE == 1
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}

D. [ABC134F] Permutation Oddness

题目大意

定义一个 \(1 \sim n\) 的排列 \(p\) 的「怪异度」为

\[\sum_{i=1}^n|p_i-i| \]

求「怪异度」为 \(m\)\(1 \sim n\) 的排列数,答案对 \(10^9+7\) 取模。

思路

考虑把 \(p_i\)\(i\) 看作小球与盒子,方便题意理解。

考虑球与盒子的匹配。

假设球在左侧,盒子在右侧,他们构成了一个二分图。

从上到下顺着排列每组球与盒子,球与盒子之间有一条横线。

我们发现,假设第 \(i\) 个盒子与 \(j\) 个球相连,他们之间的距离为 \(\left\lvert i - j \right\rvert\),他们产生的贡献相当于从 \(i\)\(j\) 的连线穿过的横线的数量。

那么我们考虑状态如何设计,记 \(dp_{i,j,k}\) 表示已经匹配了前 \(i\) 行,有 \(j\) 组球与盒子未匹配,怪异度为 \(k\) 的方案数。

那么初始值为 \(dp_{0,0,0}=1\),答案为 \(dp_{n,0,m}\) 表示匹配了前 \(i\) 行,没有球与盒子未匹配,怪异度为 \(m\) 的方案数。

考虑转移,对于一行,有一个球和一个盒子,可以匹配 \(0,1,2\) 组三种可能,那么就分这三种情况进行转移。

匹配 \(0\)

都不匹配的话,应该是 \(dp_{i-1,j-1,k-2j}\)

考虑第二维为什么是从 \(j-1\) 个未匹配组转移过来。

先考虑我们匹配 \(1\) 组的情况,我们新加入了 \(1\) 组,即第 \(i\) 行的球与盒子,又匹配了 \(1\) 组,那么新的没有匹配的组数没有发生变化,即第二维从 \(j\) 转移到 \(j\)

那么匹配 \(2\) 组的情况,相比于匹配 \(1\) 组的情况多匹配了一组,所以要从 \(j + 1\) 转移到 \(j\);同理,匹配 \(0\) 组的情况就要从 \(j-1\) 转移到 \(j\)

再考虑第三维。原本前面那些没有被匹配的盒子与球是可能被匹配到第 \(i\) 行及以后的,但是现在我们考虑的转移第 \(i\) 行并没有匹配这 \(j\) 组盒子与球。

那么这 \(j\) 组盒子与球只能匹配到第 \(i +1\) 行及以后,那么相等于我们前面的 \(j\) 组球与盒子又需要多穿过一条横线,那么总共 \(2j\) 个物品,就使得怪异值增加了 \(2j\),所以从 \(k-2j\) 转移过来。

匹配 \(1\)

将第 \(i\) 个球与前面的 \(i-1\) 行未被匹配的 \(j\) 个盒子进行匹配,有 \(j\) 种选择,每种选择的方案数为 \(j \times dp_{i-1,j,k-2j}\)

用第 \(i\) 个盒子去匹配球的方案数同理。

\(i\) 个球连第 \(i\) 个盒子的方案数单独处理,为 \(dp_{i-1,j,k-2j}\)

匹配 \(2\)

如果要匹配两组,那么第 \(i\) 行的球与盒子之间不能相互选择。

\(i\) 行的球与前 \(i-1\) 行的 \(j+1\) 个未匹配的盒子转移过来,盒子同理,根据乘法原理,有 \((j + 1)^2dp_{i-1,j+1,k-2j}\) 种方案。

状态转移方程

\[dp_{i,j,k}=j \times dp_{i-1,j,k-2j}+j \times dp_{i-1,j,k-2j}+dp_{i-1,j,k-2j}+(j+1)^2 \times dp_{i-1,j+1,k-2j}+dp_{i-1,j-1,k-2j} \]

Code

#include <bits/stdc++.h>

using namespace std;

typedef long long MainType;

const int N = 55;
const int Mod = 1e9 + 7;

int n,m;

MainType dp[N][N][N * N];
// dp[i][j][k]
// 对于前 i 组有 j 组没有配对,怪异度为 k 的方案数 

int main() {
#ifdef ONLINE_JUDGE == 1
    freopen("genshin.in","r",stdin);
    freopen("genshin.out","w",stdout);
#endif

    cin >> n >> m;

    dp[0][0][0] = 1;

    for(int i = 1;i <= n; i++) {
        for(int j = 0;j <= i; j++) {
            for(int k = j * 2;k <= m; k++) {
                if(j < 1)
                    dp[i][j][k] = (dp[i - 1][j + 1][k - j * 2] * (j + 1) % Mod * (j + 1) % Mod + dp[i - 1][j][k - j * 2] * (j * 2 + 1) % Mod) % Mod;
                else
                    dp[i][j][k] = (dp[i - 1][j + 1][k - j * 2] * (j + 1) % Mod * (j + 1) % Mod + dp[i - 1][j][k - j * 2] * (j * 2 + 1) % Mod + dp[i - 1][j - 1][k - j * 2] % Mod) % Mod;
            }
        }
    }

    cout << dp[n][0][m] % Mod;

#ifdef ONLINE_JUDGE == 1
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}
posted @ 2023-08-13 21:40  -白简-  阅读(17)  评论(1编辑  收藏  举报