2022“杭电杯”中国大学生算法设计超级联赛(7)题解报告

Problem D. Black Magic

题意:

给出n个方块,每个方块左右可能是黑色或者白色,如果两个相邻面都是黑色,这两个方块会连在一起,问最大和最小的连通块数量。

题解:

分类讨论,对于连通块最多的情况,要让能够相连的方块对尽量少。
可以发现,所有L可以全放在最左边,所有R可以全放在最右边,这样他们就不会和中间的方块相连,之后
尽可能用E把B隔开即可。对于连通块最少的情况,要让能够相连的方块对尽可能多。可以先把所有B拼在一起,之后可以把一个L和一个R分为一组拼起来。注意如果有至少一组LR并且也有至少一块B的话,可以把一组LR拆开拼在B的两边,这样可以再减少一个连通块。

代码:

#include<iostream>
#include<algorithm>
#define int long long
using namespace std;

int e, l, r, b;

void solve(){
    cin >> e >> l >> r >> b;
    if(b){
        if(l + r) cout << e + 1 + max(l - 1, r - 1) << ' ';
        else cout << e + 1 << ' ';
    }
    else {
        if(l + r)cout << e + max(l, r) << ' ';
        else cout << e << ' ';
    }
    if(b > e + 1) cout << e + l + r + b - (b - (e + 1)) << '\n';
    else cout << e + l + r + b << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem H. Triangle Game

题意:

给出三角形的三条边,每个人每次可以选择一条边并减少这条边,不能操作的人输。问先手是否必胜。

题解:

每条边减一之后异或和不为零则为必胜。

代码:

#include<iostream>
using namespace std;

int a, b, c;

void solve(){
    cin >> a >> b >> c;
    if((a - 1) ^ (b - 1) ^ (c - 1)) cout << "Win\n";
    else cout << "Lose\n";
}

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem C. Counting Stickmen

题意:

给定一张图,求符合要求的火柴人的数量。

题解:

先预处理可以做手、脚等的节点并记录有几种方案,最后组合数在去重即可。

代码:

#include<iostream>
#include<vector>
#define int long long
using namespace std;

const int N = 1e6 + 10;
const int mod = 998244353;

int n, u, v, ans;
int d[N], shou[N], tui[N];
vector<int> edg[N];

int C2(int x){
    return (x * (x - 1) / 2) % mod;
}

void init(){
    ans = 0;
    for(int i = 1; i <= n; i++){
        d[i] = tui[i] = shou[i] = 0;
        edg[i].clear();
    }
}

void dfs1(int u, int fa){
    for(int v : edg[u]){
        shou[u] += d[v] - 1;
        if(d[v] >= 3) tui[u] += C2(d[v] - 1);
        if(v == fa) continue;
        dfs1(v, u);
    }
}

void dfs2(int u, int fa){
    for(int v : edg[u]){
        if(d[v] >= 3 && d[u] >= 4) (ans += C2(d[v] - 1) * (C2(shou[u] - (d[v] - 1)) - (tui[u] - C2(d[v] - 1)) + mod) % mod * (d[u] - 3) % mod) %= mod;
        if(v == fa) continue;
        dfs2(v, u);
    }
}

void solve(){
    init();
    cin >> n;
    for(int i = 1; i < n; i++){
        cin >> u >> v;
        edg[u].push_back(v);
        edg[v].push_back(u);
        d[u] ++, d[v] ++;
    }
    dfs1(1, 0); dfs2(1, 0);
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem F. Sumire

题意:

计算一个式子

式子太难打解释也很复杂,总而言之我很懒

题解:

数位DP,用dp[i][j][0/1][0/1]表示已经统计了前i位,其中有j个位上的d,是否取数字上界,以及是否有前导零即可,用记忆化搜索维护转移。

代码:

#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define SZ(x) ((int)x.size())
#define lowbit(x) x&-x
#define pb push_back
#define ALL(x) (x).begin(),(x).end()
#define UNI(x) sort(ALL(x)),x.resize(unique(ALL(x))-x.begin())
#define GETPOS(c,x) (lower_bound(ALL(c),x)-c.begin())
#define LEN(x) strlen(x)
#define MS0(x) memset((x),0,sizeof((x)))
#define Rint register int
#define ls (u<<1)
#define rs (u<<1|1)
typedef unsigned int unit;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> Vi;
typedef vector<ll> Vll;
typedef vector<pii> Vpii;
template<class T> void _R(T &x) { cin >> x; }
void _R(int &x) { scanf("%d", &x); }
void _R(ll &x) { scanf("%lld", &x); }
void _R(ull &x) { scanf("%llu", &x); }
void _R(double &x) { scanf("%lf", &x); }
void _R(char &x) { scanf(" %c", &x); }
void _R(char *x) { scanf("%s", x); }
void R() {}
template<class T, class... U> void R(T &head, U &... tail) { _R(head); R(tail...); }
template<class T> void _W(const T &x) { cout << x; }
void _W(const int &x) { printf("%d", x); }
void _W(const ll &x) { printf("%lld", x); }
void _W(const double &x) { printf("%.16f", x); }
void _W(const char &x) { putchar(x); }
void _W(const char *x) { printf("%s", x); }
template<class T,class U> void _W(const pair<T,U> &x) {_W(x.fi);putchar(' '); _W(x.se);}
template<class T> void _W(const vector<T> &x) { for (auto i = x.begin(); i != x.end(); _W(*i++)) if (i != x.cbegin()) putchar(' '); }
void W() {}
template<class T, class... U> void W(const T &head, const U &... tail) { _W(head); putchar(sizeof...(tail) ? ' ' : '\n'); W(tail...); }
const int MOD=1e9+7,mod=998244353;
ll qpow(ll a,ll b) {ll res=1;a%=MOD; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%MOD;a=a*a%MOD;}return res;}
const int MAXN=5e5+10,MAXM=1e7+10;
const int INF=INT_MAX,SINF=0x3f3f3f3f;
const ll llINF=LLONG_MAX;
const int inv2=(MOD+1)/2;
const int Lim=1<<20;

template <int _P>
struct Modint
{
    static constexpr int P=_P;
private :
    int v;
public :
    Modint() : v(0){}
    Modint(ll _v){v=_v%P;if(v<0)v+=P;}
    explicit operator int() const {return v;}
    explicit operator long long() const {return v;}
    explicit operator bool() const {return v>0;}
    bool operator == (const Modint &o) const {return v==o.v;}
    bool operator != (const Modint &o) const {return v!=o.v;}
    Modint operator - () const {return Modint(v?P-v:0);}
    Modint operator + () const {return *this;}
    Modint & operator ++ (){v++;if(v==P)v=0;return *this;}
    Modint & operator -- (){if(v==0)v=P;v--;return *this;}
    Modint operator ++ (int){Modint r=*this;++*this;return r;}
    Modint operator -- (int){Modint r=*this;--*this;return r;}
    Modint & operator += (const Modint &o){v+=o.v;if(v>=P)v-=P;return *this;}
    Modint operator + (const Modint & o)const{return Modint(*this)+=o;}
    Modint & operator -= (const Modint & o){v-=o.v;if(v<0)v+=P;return *this;}
    Modint operator - (const Modint &o)const {return Modint(*this)-=o;}
    Modint & operator *=(const Modint & o){v=(int)(((ll)v)*o.v%P);return *this;}
    Modint operator * (const Modint & o)const {return Modint(*this)*=o;}
    Modint & operator /= (const Modint & o){return (*this)*=o.Inv();}
    Modint operator / (const Modint & o)const{return Modint(*this)/=o;}
    friend Modint operator + (const Modint &x,const ll &o) {return x+(Modint)o;}
    friend Modint operator + (const ll &o,const Modint &x) {return x+(Modint)o;}
    friend Modint operator - (const Modint &x,const ll &o) {return x-(Modint)o;}
    friend Modint operator - (const ll &o,const Modint &x) {return (Modint)o-x;}
    friend Modint operator * (const Modint &x,const ll &o) {return x*(Modint)o;}
    friend Modint operator * (const ll &o,const Modint &x) {return x*(Modint)o;}
    friend Modint operator / (const Modint &x,const ll &o) {Modint c=o;return x*c.Inv();}
    friend Modint operator / (const ll &o,const Modint &x) {Modint c=o;return c*x.Inv();}
    Modint operator ^ (ll o)const{Modint r=1,t=v;while(o){if(o&1)r*=t;t*=t;o>>=1;}return r;}
    Modint operator ~ (){return (*this)^(P-2);}
    Modint Inv() const{return (*this)^(P-2);}
};

using mi=Modint<MOD>;

template<int P>
void _W(Modint<P> x){printf("%d",(int)x);}

template<int P>
void _R(Modint<P> &x){ll t;scanf("%lld",&t);x=t;}

mi dp[75][75][2][2],vis[75][75][2][2];;
ll t;
int s[75],k,b,d,n,m,c;

mi dfs(int dep,int tot,int lim,bool zero)
{
    if(dep==m+1&&tot==0)return 1;
    if(dep==m+1)return 0;
    if(tot<0)return 0;
    if(vis[dep][tot][lim][zero])return dp[dep][tot][lim][zero];
    vis[dep][tot][lim][zero]=1;
    int up=lim?s[dep]:b-1;
    int ct=0,i=0;
    int c=(i==d);
    if(zero&&(d==0))c=0;
    dp[dep][tot][lim][zero]+=dfs(dep+1,tot-c,lim&&(s[dep]==i),zero);
    ct++;
    if(i!=d&&d<=up)
    {
        ct++;
        i=d;
        int c=(i==d);
        if(zero&&(d==0))c=0;
        dp[dep][tot][lim][zero]+=dfs(dep+1,tot-c,lim&&(s[dep]==i),0);
    }
    if(i!=up)
    {
        ct++;
        i=up;
        dp[dep][tot][lim][zero]+=dfs(dep+1,tot,lim&&(s[dep]==i),zero&&(i==0));
    }
    dp[dep][tot][lim][zero]+=dfs(dep+1,tot,0,0)*max(0,up-ct+1);
    return dp[dep][tot][lim][zero];
}


mi calc(bool f)
{
    MS0(dp);MS0(vis);
    R(t);
    m=0;
    t-=f;
    while(t)
    {
        s[++m]=t%b;
        t/=b;
    }
    reverse(s+1,s+m+1);
    mi ans=0;
    for(int i=1;i<=m;i++)
    {
        mi t=i;
        ans+=dfs(1,i,1,1)*(t^k);
    }
    return ans;
}

void solve()
{
    R(k,b,d);
    mi ans=calc(1);
    ans=calc(0)-ans;
    W(ans);
}

int main()
{
    srand(time(0));
    int T=1;
    scanf("%d",&T);
    for(int kase=1;kase<=T;kase++)
    {
        //printf("Case #%d: ",kase);
        solve();
    }
    return 0;
}

Problem B. Independent Feedback Vertex Set

题解:

答案必须包含每个三元环中的恰好一个点,因为一个点都不选则会破坏森林约束,选至少两个则会破坏
独立集约束。
同时对于一对有至少两个公共点的三元环,确定了答案包含其中一个的某个点之后另一个也随之确定了。
因此答案只可能有三种,分别对应图中唯一的三染色方案(去重后)中的每一种颜色的点。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
#define int long long
using namespace std;

int n;

void solve(){
    cin >> n;
    vector<int> w(n), c(n, 0);
    c[0] = 0, c[1] = 1, c[2] = 2;
    for(int i = 0; i < n; i ++) cin >> w[i];
    for(int i = 3; i < n; i ++){
        int j, k;
        cin >> j >> k;
        j --, k --;
        c[i] = (3 - c[j] - c[k]) % 3;
    }
    int ans[3] = {0, 0, 0};
    for(int i = 0; i < n;  i++) ans[c[i]] += w[i];
    int res = max({ans[0], ans[1], ans[2]});
    cout << res << '\n';
}

signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        solve();
    }
}

Problem I. Counting Good Arrays

题解:

考虑固定n和m,此时数组中都是m的因子,且最后一个位置为m。将每个ai除以ai-1之后对每个位置上的数做质因子分解,可以观察到答案对于每个质因子是互相独立的,即固定n时答案是一个关于m的积性函数。固定m时,设m中质因子p的幂为e,则方案数等于将e个p分给n-1个位置的方案数。显然这是一个组合数,且可以视作关于n的一个e次多项式。因此任选e+1个 ,对每个n用min25 算一次即可将答案插值出来。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

const int N = 500001;
const int P = 1000000007;

typedef vector<int> vi;

inline int add(int a, int b) { int r = a + b; return r < P ? r : r - P; }
inline int sub(int a, int b) { int r = a - b; return r < 0 ? r + P : r; }
inline int mul(int a, int b) { return 1ll * a * b % P; }
inline int qpm(int a, int b) {
    int r = 1;
    do if (b & 1) r = mul(r, a);
    while (a = mul(a, a), b >>= 1);
    return r;
}
inline int inv(int x) { return qpm(x, P - 2); }

int invs[N], fac[N], ifac[N];
int binom(int n, int k) { return mul(fac[n], mul(ifac[n - k], ifac[k])); }
void ginv() {
    invs[1] = 1; fac[0] = ifac[0] = 1;
    for (int i = 2; i != N; ++i) invs[i] = mul(invs[P % i], (P - P / i));
    for (int i = 1; i != N; ++i) fac[i] = mul(fac[i - 1], i);
    for (int i = 1; i != N; ++i) ifac[i] = mul(ifac[i - 1], invs[i]);
}

int lintp(const vector<int>& x, const vector<int>& y, int k) {
    int ans = 0, tt = x.size();
    for (int i = 0; i != tt; ++i) {
        if (k == x[i])
            return y[i];
        int u = 1, v = 1;
        for (int j = 0; j != tt; ++j) {
            if (i == j) continue;
            u = mul(u, sub(k, x[j]));
            v = mul(v, sub(x[i], x[j]));
        }
        ans = add(ans, mul(y[i], mul(u, inv(v))));
    }
    return ans;
}

namespace min25 {

const int N = 1000005;
bool ip[N]; vector<int> ps;
void sieve() {
    fill_n(ip, N, 1); ip[1] = 0;
    for (int i = 2; i < N; ++i) {
        if (ip[i]) ps.push_back(i);
        for (int j : ps) {
            if (i * j >= N) break;
            ip[i * j] = 0;
            if (i % j == 0) break;
        }
    }
}

ll n, sq, w[N]; int c;
ll g[N];

inline int id(ll x) { return x ? x <= sq ? c - x + 1 : n / x : 0; }

void cal_g(ll n_) {
    n = n_; sq = sqrt(n_); c = 0;
    for (ll l = 1, r; l <= n; l = r + 1) {
        ll v = w[++c] = n / l; r = n / v;
        g[c] = (v - 1) % P;
    }
    for (int p : ps) {
        if (1ll * p * p > n) break;
        for (int j = 1; 1ll * p * p <= w[j]; ++j)
            g[j] -= g[id(w[j] / p)] - g[id(p - 1)];
    }
}

int cal_s(int n, int i, ll x) {
    int p = ps[i];
    if (x < p) return 0;
    int sum = 0;
    if (1ll * p * p > x)
        sum = mul(n, sub(g[id(x)], i == 0 ? 0 : g[id(ps[i - 1])]));
    else {
        sum = cal_s(n, i + 1, x);
        ll q = p;
        for (int e = 1; q <= x; e++, q *= p)
            sum = add(sum, mul(binom(n + e - 1, n - 1), add(cal_s(n, i + 1, x / q), 1)));
    }
    return sum;
}

int cal_sf(ll n, ll m) {
    cal_g(m);
    vector<int> x, y;
    for (int i = 1; i <= 32; ++i) {
        x.push_back(i);
        y.push_back(add(y.empty() ? 0 : y.back(), add(cal_s(i, 0, m), 1)));
    }
    return lintp(x, y, n);
}

}


int main(void) {
    ginv();
    min25::sieve();
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d %d", &n, &m);
        printf("%d\n", min25::cal_sf(n, m));
    }
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值