Codeforces Round #630 (Div. 2)

A

模拟
一只猫初始在\((x,y)\),给你\(a,b,c,d\)表示上下左右的移动次数,你能以任意次序移动,同一个点可以重复经过
有一个矩形,猫的运动不能超过矩形的边界,问你是否可以实现一条可能的路径使得猫在矩形范围内移动完所有步骤

猫猫最后的点一定是\(x+b-a,y+d-c\),只要判断它是否在矩形边界内即可,特判一下,矩形缩成一条线时,有一维不能运动

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
    IO;
    int t;
    cin >> t;
    while(t --) {
        int l,r,u,d,x,y,lx,rx,ly,ry;
        cin >> l >> r >> u >> d;
        cin >> x >> y >> lx >> ly >> rx >> ry;
        x += r - l,y += d - u;
        if((lx == rx && (l || r)) || (ly == ry && (u || d))) cout << "NO" << endl;
        else if(x >= lx && x <= rx && y >= ly && y <= ry) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

B

染色,质因数分解
给你\(1\sim m,(m<11)\)种颜色对一串序列进行染色,必须保证颜色相同的两个数的\(gcd>1\)
那么把含有相同质因数染同色就可以了,前十一个质数打表,如果两个数能被\(primers[i]\)整除,那么他们的\(gcd>1\)是显然的,因为\(gcd>=primers[i]\)
只需要输出满足条件的染色方案即可,没有要求最小的染色数

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int primers[11] = {2,3,5,7,11,13,17,19,23,29,31};
int num[20];
int main() {
    IO;
    int t;
    cin >> t;
    while(t --) {
        int n,x,cnt = 0;
        cin >> n;
        memset(num,0,sizeof num);
        vector<int> ans;
        for(int i = 0;i < n; ++i) {
            cin >> x;
            for(int j = 0;j < 11; ++j) {
                if(x % primers[j] == 0) {
                    if(!num[j]) num[j] = ++cnt;
                    ans.push_back(num[j]);
                    break;
                }
            }
        }
        cout << cnt << endl;
        for(auto i : ans) cout << i << ' ';
        cout << endl;
    }
    return 0;
}

C

贪心,字符串
给定长度为\(n\)的字符串,让你把它分成完全相等的\(k\)段,使得每段都是一个回文串,你可以修改里面的字符,修改单个字符的代价为\(1\),求最小代价
可以把长度为\(n\)的字符串直接拆成\(k\)段,叠在一起,形成一个宽\(k\)\(n/k\)的矩形,因为每一段都相等,且都是回文,所以最后的字符\(i,k-i-1\)一定相等,对于每一列\(i,k-i-1\in [0,k-1]\)统计他们两列所有字符串里面,最多的字符,然后用这两列的总数减去最多的字符就是修改的最小代价,因为\(i\)是从\(0\sim k-1\) ,每次都枚举两列,到\(i==k-1\) 另一列就是 \(0\) 所以答案要\(/2\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
string s;
int cnt[N][30];
int differ(int u,int v) {
    int ret = 0,mx = 0;
    for(int i = 0;i < 26; ++i) {
        ret += cnt[u][i] + cnt[v][i];
        mx = max(mx,cnt[u][i] + cnt[v][i]);
    }
    return ret - mx;
}
int main() {
    IO;
    int t,n,k;
    cin >> t;
    while(t --) {
        cin >> n >> k >> s;
        for(int i = 0;i < k; ++i) {
            for(int j = 0;j < 26; ++j) {
                cnt[i][j] = 0;
            }
        }
        for(int i = 0;i < n; ++i) cnt[i % k][s[i] - 'a'] ++;
        int ans = 0;
        for(int i = 0;i < k; ++i) {
            ans += differ(i, k - 1 - i);
        }
        cout << ans / 2 << endl;
    }
    return 0;
}

D

构造,位运算
给了你一个错误的DP算法,因为其中的\(max\)函数只考虑每一个数最高位的\(1\),而\(\&\)是一个位运算,所以这么搞就是错误的,给你一个\(k\),要你构造一个矩阵使得错误算法走出的答案和正确算法走出的答案相差正好为\(k\),题目规定矩阵内的数不超过\(3\times10^{5}\)
构造一个\(2\times3\)的矩阵

\[\begin{matrix} 2^{18}-1 & 2^{17} & 0\\ k & 2^{17}+k & k\\ \end{matrix} \]

只有一种方法可以走出\(k\)\(2^{18}-1=>k=>2^{17}+k=>k\),其余方法只都是\(0\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
    IO;
    int k;
    cin >> k;
    cout << 2 <<' ' << 3 << endl;
    cout << ((1 << 18) - 1) << ' ' <<(1 << 17) << ' ' << 0 << endl;
    cout << k << ' ' << ((1 << 17) + k) << ' ' << k << endl;
    return 0;
}

E

组合数学,二项式定理,快速幂
给定\(n\times m\)的矩阵,\(a_{i,j}\) 代表\((i,j)\) 的高度,你现在有两种操作
1.给任意一个格子加\(2\)
2.给相邻的两个格子加\(1\)
问你有多少种初始的方案能够使得最终的整个矩阵每一个数都相同且\(a_{i,j}\in[L,R]\)

如果相邻两个数的奇偶性不同的话,显然第二种操作方法可以改变相邻两个数的奇偶性
分类讨论
1.\(n\times m\)是奇数
(1)奇数个奇数,偶数个偶数,先用操作2把全部数字变成奇数,再用操作1使全部数字相等
(2)偶数个奇数,奇数个偶数,先用操作2把全部数字变成偶数,再用操作1使全部数字相等
所以随便分都行,对于每一个点,都有\(R - L + 1\)种选法,对整个矩形来说就是
\((R-L+1)^{n\times m}\)
2.\(n\times m\)是偶数
只要把偶数个格子给偶数,偶数个格子给奇数即可
\(a\)代表偶数的个数,\(b\)代表奇数的个数
总共的取值范围是\([L,R]\),一共有\(sum=(R - L + 1)\) 个数字
其中\(a=sum/2,b=sum-a\)

\[\sum^{n}_{i=0,i\%2=0}\tbinom{nm}{i}a^{i}b^{nm - i}\tag{1} \]

联想二项式系数的公式

\[(a+b)^{nm}=\sum_{i=0}^{nm} \tbinom{nm}{i}a^ib^{nm-i}\tag{2} \]

可以发现,我们只需要二项式系数中的偶数项即可,现在构造另外一个式子

\[(a-b)^{nm}=\sum_{i=0}^{nm}\binom{nm}{i}a^i(-b)^{nm-i}\tag{3} \]

这样构造之后,显然\((1)=((2)+(3))/2\)把奇数项消掉即可
最后的答案\(((a-b)^{nm}+(a+b)^{nm})/2\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 998244353;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
ll mdi(ll a,ll b) {// 手动取模
    ll res = (a * b - (ll)((long double)a / mod * b + 1e-8) * mod);
    if(res > 0) res -= mod;
    if(res < 0) res += mod;
    return res;
}   
ll qmi(ll a,ll b) {
    ll res = mdi(1,1);
    while(b) {
        if(b & 1) res = mdi(res,a);
        a = mdi(a,a);
        b >>= 1;
    }
    return res;
}
int main() {
    IO;
    ll n,m,l,r;
    cin >> n >> m >> l >> r;
    ll sum = (r - l + 1),a = sum / 2,b = sum - a;
    if(n * m & 1) cout << qmi(sum,n*m); 
    else cout << (qmi(a - b,n * m) + qmi(a + b,n * m)) * qmi(2,mod - 2) % mod<< endl;
    return 0;
}

F

树形DP
求所有非空边集的独立集方案
\(dp[u][1]\)表示这个点染色的独立集数目,\(dp[u][0]\)表示这个点没有染色的独立集数目
这是一颗树的状态
\(dp[u][0]=\prod(dp[v][1]+dp[v][0])\)
\(dp[u][1]=\prod dp[v][0]\)
这个要求所有子图的独立集,用是否和子节点断开来表示一种新的子图
\(dp[u][2]\) 表示\(u\)点和所有的子节点都断开,那么它的方案数就是子节点方案数的乘积
\(dp[u][2]=\prod f[v]\)
\(f[v]\) 表示父亲节点与\(v\)断开,\(v\)的所有方案数,如果父亲节点断开,那么当前节点随便选
\(f[v]=dp[v][0]+dp[v][1]-dp[v][2]\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n' 
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 3e5 + 10;
const int mod = 998244353;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int h[N],e[N<<1],ne[N<<1],idx;
ll dp[N][3];
void add(int a,int b) {
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}
void dfs(int u,int fa) {
    dp[u][0] = dp[u][1] = dp[u][2] = 1;
    for(int i = h[u];i != -1;i = ne[i]) {
        int j = e[i];
        if(j == fa) continue;
        dfs(j,u);
        ll fv = (dp[j][0] + dp[j][1] - dp[j][2] + mod) % mod;
        dp[u][0] = dp[u][0] * (dp[j][0] + dp[j][1] + fv) % mod;
        dp[u][1] = dp[u][1] * (dp[j][0] + fv) % mod;
        dp[u][2] = dp[u][2] * fv % mod;
    }
}
int main() {
    IO;
    int n,a,b;
    memset(h,-1,sizeof h);
    cin >> n;
    for(int i = 0;i < n - 1; ++i) {
        cin >> a >> b;
        add(a,b);
        add(b,a);
    }
    dfs(1,0);
    cout << (dp[1][0] + dp[1][1] - dp[1][2] - 1 + mod) % mod << endl;
    return 0;
}
posted @ 2020-04-06 01:03  lukelmouse  阅读(110)  评论(0编辑  收藏  举报