2020CCPC长春站部分题解(pre)

A题

背包

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
int a[] = {0 , 1 , 6 , 28 , 88 , 198 , 328 , 648} ;
int b[] = {0 , 8 , 18 , 28 , 58 , 128 , 198 , 388} ;
int dp[8][maxn] ;
int main()
{
    ios ;
    rep(i , 1 , 7)  rep(j , 0 , 2000)  
    {
        if(j < a[i])  dp[i][j] = dp[i - 1][j] ;
        else  dp[i][j] = max(dp[i - 1][j] , dp[i - 1][j - a[i]] + b[i]) ;
    }
    int n ;
    cin >> n ;
    cout << dp[7][n] + n * 10 << '\n' ;
    return 0 ;
}

D题

\dpi{150}a_n = c^{popcount(n)}

枚举固定的前缀,计算后缀不同个数1的贡献。

时间复杂度:O(len^2)

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 1e9 + 7 ;
const int maxn = 2e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
int c ;
char s[maxn] ;
struct Easymath
{
    ll qpow(ll a , ll b) //快速幂
    {
        if(b < 0)  return 0 ;
        ll ans = 1 ; 
        a %= mod ;
        while(b)
        {
            if(b & 1)  ans = (ans * a) % mod ;
            b >>= 1 , a = (a * a) % mod ;
        }
        return ans % mod ;
    }
    ll ksc_log(ll x , ll y , ll mod) //快速乘
    {
        x %= mod , y %= mod ;
        ll ans = 0;
        while(y)
        {
            if(y & 1) ans = (ans + x) % mod ; 
            y >>= 1 ;
            x = (x + x) % mod ;
        }
        return ans;
    }
    ll ksc_O1(ll x , ll y , ll mod) //快速乘
    {
        x %= mod , y %= mod ;
        ll z = (ld)x * y / mod ;
        ll ans = x * y - z * mod ;
        if(ans < 0)  ans += mod ;
        else if(ans >= mod)  ans -= mod ;
        return ans ;
    }
    int cnt = 0 ;
    bool vis[maxn] ;
    int prime[maxn] ;
    void get_prime(int up) //素数筛
    {
        memset(vis , 0 , sizeof(vis)) ;
        vis[1] = 1 ;
        for(int i = 2 ; i <= up ; i ++)
        {
            if(!vis[i]) 
            prime[++ cnt] = i ;
            for(int j = 1 ; j <= cnt && i * prime[j] <= up ; j ++)
            {
                vis[i * prime[j]] = 1 ;
                if(i % prime[j] == 0) break ;
            }
        }
    }
    //begin 判定大素数
    ll mul(ll a , ll b , ll mod)
    {
        ll ret = 0 ;
        while(b) 
        {
            if(b & 1)  ret = (ret + a) % mod ;
            a = (a + a) % mod ;
            b >>= 1 ;
        }
        return ret ;
    }
    ll pow(ll a , ll b , ll mod)
    {
        ll ret = 1 ;
        while(b) 
        {
            if(b & 1)  ret = mul(ret , a , mod) ;
            a = mul(a , a , mod) ;
            b >>= 1 ;
        }
        return ret ;
    }
    bool check(ll a , ll n)
    {
        ll x = n - 1 ;
        int t = 0 ;
        while((x & 1) == 0) 
        {
            x >>= 1 ;
            t ++ ;
        }
        x = pow(a , x , n) ;
        ll y ;
        rep(i , 1 , t)
        {
            y = mul(x , x , n) ;
            if(y == 1 && x != 1 && x != n - 1)  return 1 ;
            x = y ;
        }
        if(y != 1) return 1 ;
        return 0 ;
    }
    bool Miller_Rabin(ll n) 
    {
        if(n == 2)  return 1 ;
        if(n == 1 || !(n & 1))  return 0 ;
        const int arr[12] = {2,3,5,7,11,13,17,19,23,29,31,37} ;
        rep(i , 0 , 11) 
        {
            if(arr[i] >= n) break ;
            if(check(arr[i] , n)) return 0 ;
        }
        return 1 ;
    }
    //end 判定大素数
    ll get_inv(ll x) //逆元
    {
       return qpow(x , mod - 2) % mod ;
    }
    ll inv1[maxn] ;  //乘法逆元
    void init1(int up)
    {
       inv1[1] = 1 ;
       for(int i = 2 ; i <= up ; i ++)
         inv1[i] = (ll)(mod - mod / i) * inv1[int(mod % (ll)i)] % mod ;
    }
    ll fac[maxn] ;
    ll inv[maxn] ; //阶乘逆元
    void init(int up) 
    {
       fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
       for(int i = 2 ; i <= up ; i ++)
       {
         fac[i] = fac[i - 1] * i % mod ;
         inv[i] = -inv[mod % i] * (mod / i) % mod ;
         while(inv[i] < 0) inv[i] += mod ;
       }
       for(int i = 2 ; i <= up ; i ++)  
         inv[i] = inv[i] * inv[i - 1] % mod ;
    }
    ll C(int n , int m)
    {
        return fac[n] * inv[m] % mod * inv[n - m] % mod ;
    }
} em ;
int main()
{
    ios ;
    em.init(10000) ;
    cin >> s + 1 >> c ;
    int len = strlen(s + 1) ;
    int lst = 0 ;
    ll ans = 0 ;
    rep(i , 1 , len)
    {
        if(s[i] == '0')  continue ;
        ll res = 0 ;
        rep(j , 0 , len - i)  res += em.C(len - i , j) * em.qpow(c , lst + j) % mod , res %= mod ;
        ans += res ;
        ans %= mod ;
        lst ++ ;
    }
    ans += em.qpow(c , lst) ;
    ans %= mod ;
    cout << ans << '\n' ;
    return 0 ;
}

F题

dsu on tree维护一个数组t[][][]。t[i][j][k]表示当前子树内a[u]=i且u的第j位是k的u的个数。

这个东西没办法直接维护的,但是对于j,你没必要知道i是什么,假如j的第k位是0,那么你需要知道第k位是1的i的个数即可。

因此把i直接拆成20位,就可以统计答案了。

我个人理解dsu on tree对于处理子树间的贡献和子树内的贡献这两种不同的题型有两种不同的写法,需要特别注意。

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 1e5 + 10 ;
const int maxm = 1e6 + 1e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
int n , a[maxn] ;
int c[maxn] ;
vector<int> g[maxn] ;
ll ans = 0 ;
struct Dsu_on_tree
{
    int siz[maxn] , son[maxn] ;
    int flag ;
    int t[maxm][20][2] ; //t[i][j][k]表示a[u]=i且u的第j位是k的u的个数
    void init() 
    {
        flag = 0 ;
        memset(siz , 0 , sizeof(siz)) ;
        memset(son , 0 , sizeof(son)) ;
        memset(t , 0 , sizeof(t)) ;   
        rep(i , 0 , 19)  c[i] = (1 << i) ;
    }
    void dfs1(int f , int u)
    {
        siz[u] = 1 ;
        for(auto v : g[u])
        {
            if(v == f) continue ;
            dfs1(u , v) ; 
            siz[u] += siz[v] ;
            if(siz[v] > siz[son[u]]) son[u] = v ;
        }
    }
    void add(int u , int x)
    {
        int tmp = a[u] ;
        rep(j , 0 , 19)  t[tmp][j][u % 2] += x , u /= 2 ;
    }
    void dfs3(int fa , int u , int lca)
    {
        int s = (a[u] ^ a[lca]) ;
        int tmp = u ;
        rep(j , 0 , 19)  ans += 1ll * t[s][j][1 - (tmp % 2)] * c[j] , tmp /= 2 ;
        for(auto v : g[u])
        {
            if(v == fa)  continue ;
            dfs3(u , v , lca) ;
        }
    }
    void dfs4(int fa , int u , int x)
    {
        add(u , x) ;
        for(auto v : g[u])
        {
            if(v == fa)  continue ;
            dfs4(u , v , x) ;
        }
    }
    void calc(int f , int u , int x)
    {
        for(auto v : g[u])
        {
            if(v == f || v == flag) continue ;
            if(x == 1)  dfs3(u , v , u) ;
            dfs4(u , v , x) ;
        }
        add(u , x) ;
    }
    void dfs2(int f , int u , int keep)
    {
        for(auto v : g[u])
        {
            if(v == f || v == son[u]) continue ;
            dfs2(u , v , 0) ;
        }
        if(son[u])  dfs2(u , son[u] , 1) , flag = son[u] ;
        calc(f , u , 1) ;
        if(son[u]) flag = 0 ;
        if(!keep)  calc(f , u , -1) ;
    }
} dsu_on_tree ;
int main()
{
    ios ;
    cin >> n ;
    rep(i , 1 , n)  cin >> a[i] ;
    rep(i , 1 , n - 1)
    {
        int u , v ;
        cin >> u >> v ;
        g[u].pb(v) , g[v].pb(u) ;
    }
    dsu_on_tree.init() ;
    dsu_on_tree.dfs1(1 , 1) ;
    dsu_on_tree.dfs2(1 , 1 , 0) ;
    cout << ans << '\n' ;
    return 0 ;
}

K题

不妨设a>b,打表后会发现(a-b) \mid a,这就可以直接O(nlogn)预处理出符合条件的二元组了。

但是还是不好直接做,不过如果打表技术再高超一点,会发现对于每个a,满足条件的b最多只有31个。

这样操作2就按秩合并,操作3就暴力修改。

PS:其实F题需要想到拆位,K题需要想到满足条件的二元组不是很多,才可以做。假如都想到了,那么K比F好写很多。不过如果都没想到,那这场就没了。

#include<bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define sz(x)  (int)x.size()
#define cl(x)  x.clear()
#define all(x)  x.begin() , x.end()
#define rep(i , x , n)  for(int i = x ; i <= n ; i ++)
#define per(i , n , x)  for(int i = n ; i >= x ; i --)
#define mem0(x)  memset(x , 0 , sizeof(x))
#define mem_1(x)  memset(x , -1 , sizeof(x))
#define mem_inf(x)  memset(x , 0x3f , sizeof(x))
#define debug(x)  cerr << #x << " = " << x << '\n'
#define ddebug(x , y)  cerr << #x << " = " << x << "   " << #y << " = " << y << '\n'
#define ios std::ios::sync_with_stdio(false) , cin.tie(0)
using namespace std ;
typedef long long ll ;
typedef long double ld ;
typedef pair<int , int> pii ;
typedef pair<ll , ll> pll ;
typedef double db ;
const int mod = 998244353 ;
const int maxn = 3e5 + 10 ;
const int inf = 0x3f3f3f3f ;
const double eps = 1e-6 ; 
int n , q , a[maxn] ;
vector<int> v[maxn] ;
set<pii> s[maxn] ;
ll ans = 0 ;
struct Dsu
{
    int pre[maxn] , siz[maxn] ;
    void init(int n)
    {
        for(int i = 1 ; i <= n ; i ++)  pre[i] = i , siz[i] = 1 ;
    }
    int find(int u) 
    {
        if(pre[u] == u)  return u ;
        return pre[u] = find(pre[u]) ;
    }
    void join(int x , int y)
    {
        int fx = find(x) ;
        int fy = find(y) ;
        if(fx != fy)  
        {
            if(siz[fx] <= siz[fy])  pre[fx] = fy , siz[fy] += siz[fx] ;
            else  pre[fy] = fx , siz[fx] += siz[fy] ;
        }       
    }
    ll cal(int fx , int t , int cnt)
    {
        ll res = 0 ;
        for(auto u : v[t])
        {
            auto it = s[fx].lower_bound({u , 0}) ;
            if(it != s[fx].end())
            {
                int tt = (*it).fi ;
                int num = (*it).se ;
                if(tt == u)  res += 1ll * num * cnt ;
            }
        }
        return res ;
    }
    void merge(int x , int y)
    {
        int fx = find(x) ;
        int fy = find(y) ;
        if(fx != fy)  
        {
            if(siz[fx] < siz[fy])  swap(fx , fy) ;
            pre[fy] = fx ;
            siz[fx] += siz[fy] ;
            for(auto u : s[fy])
            {
                int t = u.fi ;
                int cnt = u.se ;
                ans += cal(fx , t , cnt) ;
            }
            for(auto u : s[fy])
            {
                int t = u.fi ;
                int cnt = u.se ;
                auto it = s[fx].lower_bound({t , 0}) ;
                if(it != s[fx].end())
                {
                    int tt = (*it).fi ;
                    int num = (*it).se ;
                    if(tt == t)  s[fx].erase(it) , s[fx].insert({tt , num + cnt}) ;
                    else  s[fx].insert(u) ;
                }
                else  s[fx].insert(u) ;
            }
        }
    }
    void update(int x , int y)
    {
        int fx = find(x) ;
        auto it = s[fx].lower_bound({a[x] , 0}) ;
        int t = a[x] ;
        int num = (*it).se ;
        if(num == 1)  s[fx].erase(it) ;
        else  s[fx].erase(it) , s[fx].insert({t , num - 1}) ;
        //del
        ans -= cal(fx , a[x] , 1) ;
        //add

        a[x] = y ;
        ans += cal(fx , a[x] , 1) ;
        it = s[fx].lower_bound({a[x] , 0}) ;
        if(it == s[fx].end())  s[fx].insert({a[x] , 1}) ;
        else
        {
            int t = (*it).fi ;
            int cnt = (*it).se ;
            if(t == a[x])  s[fx].erase(it) , s[fx].insert({t , cnt + 1}) ;
            else  s[fx].insert({a[x] , 1}) ;
        }
    }
} dsu ;
void prework()
{
    dsu.init(n) ;
    rep(b , 1 , 200000)  for(int a = b + b ; a <= 200000 ; a += b)
    {
        int c = a - b ;
        if((a ^ c) == b)  v[a].pb(c) , v[c].pb(a) ;
    }
    rep(i , 1 , n)  s[i].insert({a[i] , 1}) ;
}
int main()
{
    ios ;
    cin >> n >> q ;
    rep(i , 1 , n)  cin >> a[i] ;
    n += q ;
    prework() ;
    while(q --)
    {
        int op , x , y ;
        cin >> op >> x >> y ;
        if(op == 1)  a[x] = y , s[x].insert({y , 1}) ;
        else if(op == 2)  dsu.merge(x , y) ;
        else  dsu.update(x , y) ;
        cout << ans << '\n' ;
    }
    return 0 ;
}

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值