2020-2021 Winter Petrozavodsk Camp, UPC contest 部分题解 ABI

A. Adjacent Rooks

题意

给定一个n*n大小的棋盘,放入n个棋子使得每个棋子占一行一列(任意2个棋子不在同一行且不在同一列),询问恰好有k对棋子斜对角相邻的摆放方案数

思路

可以看出来,可以用行号来表示每一列棋子的位置,生成一个全排列,每次交换时候都是全排列元素的置换

那么,考虑一个dp转移,由 i 到 i+1 ,有把 i+1 这个数放到这个全排列的任意一个插空位置(含左右两边),也就是有i+1种插入方式

下面给出一种4转移到5的方案表示,(.表示可插入位置,X表示放置的棋子,O表示空位)

O X O O          O X O O          O X O O O
X O O O          X O O O          X O O O O
O O O X   --->   O O O X   --->   O O O O X
O O X O          O O X O          O O O X O
                . . X . .         O O X O O

接下来,我们考虑新增的棋子对原棋盘的贡献

如果前两个棋子有贡献

  • 如果顺着前最后两个棋子的方向(如上图),贡献 +1
  • 如果逆着最后两个棋子的方向(如上图将第3、4列置换),贡献 +1-1 = +0
  • 如果插入到其他地方,考虑是否有破坏贡献,如果有,贡献 -1;如果无,贡献 +0

如果前一个棋子孤立/无贡献

  • 同上考虑4种情况的贡献(具体可以看代码,写的很清楚)

那么我们可以定义一个函数

\[f(i,j,k)=val \]

其中,\(i\) 表示当前有几个棋子,\(j\) 表示当前带来的贡献恰好为 \(j\)\(k\)表示上两个棋子是正对角线排列or反对角线排列or上一个点孤立

状态转移大致为:

\[f(i+1,j+贡献,下一个状态) += f(i,j,当前状态)*情况数 \]

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<(b);++i)
#define per(i,a,b) for(int i=(b-1);i>=a;--i)
using namespace std;
using i64 = long long;
using ll = long long;
using vi = vector<int>;
#define all(x) x.begin(),x.end()
#define sz(x) x.size()
#define pb push_back

const int N = 1006,K = 1006;
const i64 MOD = 1e9+7;
i64 f[N][K][3] = {0};
// 0: OOO 1: OXO 2: OXO
//    OXO    OOX    XOO
 
void add(i64&o1,i64 o2) {
    o1 += o2;
    if(o1>=MOD) o1-=MOD;
}
 
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    
    f[1][0][0] = 1;
    for(int i=1;i<=1000;++i) {
        for(int j=0;j<i;++j) {
            add(f[i+1][j+1][1], f[i][j][0]);
            add(f[i+1][j+1][2], f[i][j][0]);
            if(j) add(f[i+1][j-1][0], j*f[i][j][0]%MOD);
            add(f[i+1][j][0], (i+1-2-j)*f[i][j][0]%MOD);
 
            add(f[i+1][j+1][1], f[i][j][1]);
            add(f[i+1][j][2], f[i][j][1]);
            if(j) add(f[i+1][j-1][0], (j-1)*f[i][j][1]%MOD);
            add(f[i+1][j][0], (i+1-1-j)*f[i][j][1]%MOD);
 
            add(f[i+1][j+1][2], f[i][j][2]);
            add(f[i+1][j][1], f[i][j][2]);
            if(j) add(f[i+1][j-1][0], (j-1)*f[i][j][2]%MOD);
            add(f[i+1][j][0], (i+1-1-j)*f[i][j][2]%MOD);
        }
    }
    int t;
    cin >> t;
    while(t--) {
        int n,k;
        cin >> n >> k;
        cout << (f[n][k][0] + f[n][k][1] + f[n][k][2]) % MOD << '\n';
    }
 
    return 0;
}

B. Beautiful Permutation (非正解)

据说是最难的题,但是可以乱搞

而且题解上写的完全看不懂

题意

给你一个n,构造一个n长度大小的 0到n-1 的全排列a,得到

\[b[i] = |a[i]-i|, i\in [0,n) \]

使得数组b亦为全排列

思路

乱想一下,对a、b求个和

\[\sum b_i = \frac{n \cdot(n-1)}{2} \]

\[\sum |a_i-i| = \left\{\begin{matrix}a_i-i, &\geq0\\ i-a_i,&<0 \end{matrix}\right. \]

显然,要使得两式相等,负的部分得为

\[X = \frac{n \cdot(n-1)}{4} \]

因为\(n\)是整数,所以显然 \(n\ mod \ 4 == 0 \ or\ 1\) 时才能整除,其他情况无解

那么考虑构造样例

0 1 2 3  // i
3 0 2 1  // a_i
3 1 0 2  // b_i

没什么思路,构造个长一点的

0 1 2 3 4 5 6 7 8
8 7 6 5 4 4 3 2 1  // 多了个4
8 6 4 2 0 1 3 5 7  // b_i

除了中间那个4,基本构造完成了,看样子乱搞一下中间部分应该行

然后考虑到要满足\(X\)那个条件,我们尽量让它好看一点,为O部分和\的部分的一半就是我们要减去的东西

                 *                   *
               * *                 * *
             * * *               * * *
           * * * *             * * * *
         * * * * *           O \ * * *
       * * * * * *         O O O \ * *
     * * * * * * *       O O O O O \ *
   * * * * * * * *     O O O O O O O \
 0 1 2 3 4 5 6 7 8   0 1 2 3 4 5 6 7 8

先把中间干烂

0 1 2 3 4 5 6 7 8
8 7 6   0   3 2 1
8 6 4   4   3 5 7  // b_i

好像下面又多了个4。。。怎么办,再退后一步,先搞其他的

0 1 2 3 4 5 6 7 8
8 7     0     2 1
8 6     4     5 7  // b_i
0 1 2 3 4 5 6 7 8
8 7 5   0     2 1
8 6 3   4     5 7  // b_i
                 *
               * *
             * * *
           * * * *
         O * * * *
       O O O * * *
     - O O O - O *
   - - O O O - - O
 0 1 2 3 4 5 6 7 8

6还是没地方放,然而我们还有3+4+3+2没减,考虑枚举一下位置吧,放3下面肯定不合适,5下面或者6下面

对了,刚好缺个0,要不丢6下面?还解决了4+2,接下来减掉2个3就好,刚好3没用过

0 1 2 3 4 5 6 7 8
8 7 5   0   6 2 1
8 6 3   4   0 5 7  // b_i
0 1 2 3 4 5 6 7 8  // i
8 7 5 4 0 3 6 2 1  // a_i
8 6 3 1 4 2 0 5 7  // b_i

完美!

回忆一下怎么构造的:

  • 先是左一次右一次,凑 8765
  • 然后4用 4-0 来凑
  • 接下来凑0,这里使用 6-6
  • 然后再用同样的办法凑 321
  • 注:中间左边的数,全都是a[i]>i,右边除了6全是i>a[i]

最终达到下图中间这座小山的左右两边分别用最小的几个数凑出,然后中间部分那几个和斜线部分除以2用一个数来凑

                 *
               * *
             * * *
           * * * *
         | \ * * *
       O | O \ * *
     O O | O O \ *
   O O O | O O O \
 0 1 2 3 4 5 6 7 8

检验一下12和13,符合预期,上机!(虽然训练时候没想出来x)

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<b;++i)
using namespace std;
using i64 = long long;

const int N = 1e6+6;
int a[N];

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);
    
    int n;
    cin >> n;
    if(n%4==2 || n%4==3) {
        cout << "NO";
        return 0;
    }
    cout << "YES\n";

    a[n-1-n/4] = n-1-n/4;
    for(int i=0;i<n>>2;++i) {
        a[i] = n-i-1;
        a[n-1-i] = i+1;
    }

    for(int i=0;n/4+i<n-1-n/4-i-1;++i) {
        a[n/4+i] = n-1-n/4-i-1;
        a[n-1-n/4-i-1] = n/4+i+1;
    }
    a[(n-1)/2] = 0;

    for(int i=0;i<n;++i) {
        cout << a[i] << ' ';
    }

    return 0;
}

I. Interesting Scoring Systems

细节题,想不清楚就容易WA,建议和队友多讨论讨论

题意

给了3种比赛系统,2种为计分系统,A:win+2,lose+0,draw+1,B:win+3,lose+0,draw+1,然后第三种是,类似大逃杀模式,只要1有打败2,就可以视作1打败了2打败的队伍(连出有向边),最后决出胜者(有且仅有1位)。现在给你他们在AB系统下的分数,问C系统是否可能让第一个人取胜。

思路

分类:

  • n==1,yes
  • n==2,第一个人不能没赢过,第二个人不能赢
  • n==3,第一个人不能没赢过,总的连出有向边个数不能少于n-1

做完了。。。嗯…就这样……

至于如果不知道为什么,不是题没读懂就是细节没考虑清楚,举例就知道了

代码

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1e6 + 10;
using ll = long long;
using pii = pair<int,int>;

const int N = 1e6+6;
int a[N],b[N];

void sol() {
	int n;
	cin >> n;
	for(int i=0;i<n;++i) cin >> a[i];
	for(int i=0;i<n;++i) cin >> b[i], a[i]=b[i]-a[i];
	
	if(n==1) {
		cout << "YES\n";
		return;
	}
	if(n==2) {
		if(a[0]>=1 && a[1]==0) {
			cout << "YES\n";
		}else {
			cout << "NO\n";
		}
		return;
	}
	
	int tot = 0;
	for(int i=0;i<n;++i) {
		tot += a[i];
	}
	if(a[0]>=1 && tot>=n-1) {
		cout << "YES\n";
	}else {
		cout << "NO\n";
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	int t;
	cin >> t;
	while(t--) sol();
	
	return 0;
}
posted @ 2021-11-11 22:36  LacLic  阅读(495)  评论(0编辑  收藏  举报