2016-2017 ACM-ICPC CHINA-Final 部分题解
发布日期:2021-05-06 15:23:49 浏览次数:22 分类:原创文章

本文共 23985 字,大约阅读时间需要 79 分钟。

Problem A. Number Theory Problem

温暖的签到题。

#include<bits/stdc++.h>using namespace std ;int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        int n ;        cin >> n ;        cout << "Case #" << cas << ": " << n / 3 << '\n' ;    }    return 0 ;}

 

Problem B. Hemi Palindrome

用数位dp的思想写即可。容斥计算前i-1位确定,第i位是0,后n-i位不确定的半回文串个数。 

容斥计算的方法是奇回文+偶回文-奇偶回文。

细节太多,写的我欲仙欲死。

#include<bits/stdc++.h>using namespace std ;const int maxn = 1e5 + 10 ;const long long inf = 3e18 ;int n ;long long k ;bool flag ;int a[maxn] ;long long p[105] ;bool flag1 , flag0 ;long long qpow(int y){    if(y >= 60)  return 1e18 + 10 ;    return p[y] ;}long long cal1(int i) //所有奇数对称{    if(!flag1)  return 0 ;    int len1 = (i + 1) / 2 ;    int len2 = (n + 1) / 2 ;    if(len1 * 2 >= len2) //奇数没有自由位    {        int len = n / 2 - i / 2 ; //偶数自由位        len = max(len , 0) ;        return qpow(len) ;    }    else    {        int len = n / 2 - i / 2 ; //偶数自由位        len = max(len , 0) ;        int len3 = (len2 - len1 * 2 + 1) / 2 ; //奇数自由位        len3 = max(len3 , 0) ;        return qpow(len3 + len) ;    }}long long cal0(int i) //所有偶数对称{    if(!flag0)  return 0 ;    int len1 = i / 2 ;    int len2 = n / 2 ;    if(len1 * 2 >= len2) //偶数没有自由位    {        int len = (n + 1) / 2 - (i + 1) / 2 ; //奇数自由位        len = max(len , 0) ;        return qpow(len) ;    }    else    {        int len = (n + 1) / 2 - ((i + 1) / 2) ; //奇数自由位        len = max(len , 0) ;        int len3 = (len2 - len1 * 2 + 1) / 2 ; //偶数自由位        len3 = max(len3 , 0) ;        return qpow(len3 + len) ;    }}long long cal10(int i){    if(!flag1 || !flag0)  return 0 ;    int num = 0 ;    int len1 = (i + 1) / 2 ;    int len2 = (n + 1) / 2 ;    if(len1 * 2 < len2)  num += (len2 - len1 * 2 + 1) / 2 ; //奇数自由位    int len3 = i / 2 ;    int len4 = n / 2 ;    if(len3 * 2 < len4)  num += (len4 - len3 * 2 + 1) / 2 ; //偶数自由位    // cout << num << '\n' ;    return qpow(num) ;}long long cal(int i){    long long sum = 0 ;    sum += cal1(i) ;    if(sum >= 3 * k)  return k ;    sum += cal0(i) ;    if(sum >= 3 * k)  return k ;    sum -= cal10(i) ;    // if(i == 3)    // {    //     cout << "!!!" << ' ' << i << ' ' << cal1(i) << ' ' << cal0(i) << ' ' << cal10(i) << '\n' ;    // }    //if(sum < 0)  cout << "!!!" << ' ' << i << ' ' << cal1(i) << ' ' << cal0(i) << ' ' << cal10(i) << '\n' ;    return sum ;}int id1(int i){    return (i - 1) * 2 + 1 ;}int id0(int i){    return i * 2 ;}bool check(int i){    if(i % 2 == 1)    {        int len = (n + 1) / 2 ;        int nxt = id1(len + 1 - (i + 1) / 2) ;        // if(i == 3)        // {        //     cout << "???" << nxt << ' ' << a[nxt] << ' ' << a[i] << '\n' ;        // }        if(nxt < i && nxt >= 1 && a[nxt] != a[i])          {            flag1 = false ;            return false ;        }    }    else    {        int len = n / 2 ;        int nxt = id0(len + 1 - i / 2) ;        if(nxt < i && nxt >= 1 && a[nxt] != a[i])          {            flag0 = false ;            return false ;        }    }    return true ;}void solve(){    if(cal(0) < k)    {        flag = false ;        return ;    }    //cal10(1) ;    //cout << cal1(3) << ' ' << cal0(3) << ' ' << cal10(3) << '\n' ;    for(int i = 1 ; i <= n ; i ++)    {        bool t1 = flag1 ;        bool t0 = flag0 ;        a[i] = 0 ;        check(i) ;        // if(i == 3)        // {        //     cout << a[1] << ' ' << a[3] << ' ' << flag1 << '\n' ;        // }        long long tmp = cal(i) ;        flag1 = t1 ;        flag0 = t0 ;        //cout << i << ' ' << tmp << '\n' ;        if(tmp < k)  k -= tmp , a[i] = 1 , check(i) ;        else  a[i] = 0 , check(i) ;      }}int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    p[0] = 1 ;    for(int i = 1 ; i <= 60 ; i ++)  p[i] = p[i - 1] * 2 ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ": " ;        cin >> n >> k ;        flag = true ;        flag1 = true ;        flag0 = true ;        solve() ;        if(flag)          {            for(int i = 1 ; i <= n ; i ++)  cout << a[i] ;            cout << '\n' ;        }        else  cout << "NOT FOUND!\n" ;    }    return 0;}

 

\dpi{150} jyf写的就很小清新。

#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 1e5 + 5;int kas;int n, ans[N], dif[2], oddmid, evenmid;ll k;int count0(int l, int r){    if(l>r) return 0;    if(l&1) ++l;    if(r&1) --r;    return (r-l+2)>>1;}int count1(int l, int r){    if(l>r) return 0;    if(l%2==0) ++l;    if(r%2==0) --r;    return (r-l+2)>>1;}ll Pow2(int x) { return 1ll<<(min(62, x)); }void upd(int p, int v){    if(p%2==1 && p>oddmid)    {        int rev = 2*oddmid - p;        if(count1(1, n)%2==0) rev += 2;        dif[1] = dif[1] + v*(ans[rev]!=ans[p]);    }    else if(p%2==0 && p>evenmid)    {        int rev = 2*evenmid - p;        if(count0(1, n)%2==0) rev += 2;        dif[0] = dif[0] + v*(ans[rev]!=ans[p]);    }}void solve(){    scanf("%d%lld", &n, &k);    dif[0] = dif[1] = 0;    if(n&1) oddmid = (n+(n%4==1))>>1;    else oddmid = (n>>1) - (n%4==0);    if(n&1) evenmid = (n+(n%4==3))>>1;    else evenmid = (n>>1) + (n%4!=0);    for(int i=1; i<=n; i++)    {        ans[i] = 0;        upd(i, 1);        ll oddcnt = 0, evencnt = 0, allcnt = 0;        if(i<=oddmid) oddcnt = Pow2(n-i-count1(oddmid+2, n));        else if(!dif[1]) oddcnt = Pow2(n-i-count1(i+1, n));        if(i<=evenmid) evencnt = Pow2(n-i-count0(evenmid+2, n));        else if(!dif[0]) evencnt = Pow2(n-i-count0(i+1, n));        if(i<=max(oddmid, evenmid)) allcnt = Pow2(max(oddmid, evenmid)-i);        else if(!dif[0]&&!dif[1]) allcnt = 1;        //cout << oddcnt << ' ' << evencnt << ' ' << allcnt << '\n';        if(oddcnt+evencnt-allcnt<k)         {            k -= oddcnt + evencnt - allcnt;            upd(i, -1);            ans[i] = 1;            upd(i, 1);        }    }    printf("Case #%d: ", ++kas);    if(k==1)    {        for(int i=1; i<=n; i++) printf("%d", ans[i]);        puts("");    }    else puts("NOT FOUND!");}int main(){    int _; scanf("%d", &_);    while(_--) solve();    return 0;}

 

Problem C. Mr. Panda and Strips

预处理dp[l][r]表示[l,r]内最长不重复连续子序列长度。

考虑固定左区间的左端点,滑动左区间的右端点。左区间内的数不能在右区间出现,因此滑动左区间右端点时,右边合法区间在逐渐分裂成小区间。set维护右边合法区间即可。

时间复杂度O(n^2logn)

#include<bits/stdc++.h>using namespace std ;int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    int T ;    cin >> T ;    vector<bool> c(100000 + 10 , false) ;    vector<vector<int>> pos(100000 + 10) ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ": " ;        int n ;        cin >> n ;        vector<int> a(n + 1) ;        vector<vector<int>> dp(n + 1) ;        for(int i = 1 ; i <= n ; i ++)  cin >> a[i] , pos[a[i]].push_back(i) ;        for(int i = 1 ; i <= n ; i ++)  dp[i].resize(n + 1 , 0) ;        for(int i = 1 ; i <= n ; i ++)        {            for(int j = i ; j <= n ; j ++)            {                if(c[a[j]])  break ;                c[a[j]] = true ;                dp[i][j] = j - i + 1 ;            }            for(int j = 1 ; j <= n ; j ++)  c[a[j]] = false ;        }        for(int len = 2 ; len <= n ; len ++)            for(int i = 1 ; i + len - 1 <= n ; i ++)            {                int j = i + len - 1 ;                dp[i][j] = max({dp[i][j] , dp[i + 1][j] , dp[i][j - 1]}) ;            }        int ans = 0 ;        set<pair<int , int>> s ;        multiset<int> t ;        for(int i = 1 ; i <= n ; i ++)        {            s.clear() ;            s.insert({i , n}) ;            t.clear() ;            t.insert(dp[i][n]) ;            for(int j = i ; j <= n ; j ++)            {                if(c[a[j]])  break ;                c[a[j]] = true ;                ans = max(ans , j - i + 1) ;                for(auto u : pos[a[j]])                {                    if(u < i)  continue ;                    auto it = s.lower_bound({u , n + 1}) ;                    it -- ;                    int l = (*it).first ;                    int r = (*it).second ;                    s.erase(it) ;                    auto it2 = t.lower_bound(dp[l][r]) ;                    t.erase(it2) ;                    if(l < u)  s.insert({l , u - 1}) , t.insert(dp[l][u - 1]) ;                    if(u < r)  s.insert({u + 1 , r}) , t.insert(dp[u + 1][r]) ;                }                int tmp = 0 ;                if(t.size() > 0)  tmp = (*t.rbegin()) ;                ans = max(ans , j - i + 1 + tmp) ;            }            for(int j = 1 ; j <= n ; j ++)  c[a[j]] = false ;        }        cout << ans << '\n' ;        for(int i = 1 ; i <= n ; i ++)  pos[a[i]].clear() ;    }    return 0 ;}

 

Problem D. Ice Cream Tower

二分答案x。check时用前x个数作为x个序列的首元素。

 

#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 3e5 + 5;int kas;int n, k, cnt[N];ll a[N], ed[N];bool check(int x){    int j = x+1;    for(int i=1; i<=x; i++) ed[i] = a[i], cnt[i] = 1;    while(j<=n&&cnt[x]<k)    {        for(int i=1; i<=x; i++)        {            while(j<=n && a[j]<2*ed[i]) ++j;            if(j<=n) ++cnt[i], ed[i] = a[j], ++j;        }    }    return cnt[x]>=k;}void solve(){    scanf("%d%d", &n, &k);    for(int i=1; i<=n; i++) scanf("%lld", a+i);    sort(a+1, a+n+1);    int l = 1, r = n/k, ans = 0;    while(l<=r)    {        int mid = (l+r)>>1;        if(check(mid)) ans = mid, l = mid + 1;        else r = mid - 1;    }    printf("Case #%d: %d\n", ++kas, ans);}int main(){    int _; scanf("%d", &_);    while(_--) solve();    return 0;}

 

Problem E. Bet

\frac{a_i}{b_i}升序排列,挑选满足\sum \frac{a_i}{b_i} < 1的最大个数的前缀即可。

卡精度,需要使用long \; double

#include <bits/stdc++.h>#define fi first#define se secondusing namespace std;using db = long double;using pdd = pair<db, db>;const int N = 105;int n, kas;pdd p[N];void solve(){    scanf("%d", &n);    for(int i=1; i<=n; i++) scanf("%Lf:%Lf", &p[i].fi, &p[i].se);    sort(p+1, p+n+1, [](pdd x, pdd y) { return x.fi*(y.fi+y.se) < y.fi*(x.fi+x.se); });    int ans = 0;    db cur = 0;    for(int i=1; i<=n; i++)    {        cur += 1.0*p[i].fi/(p[i].fi+p[i].se);        if(cur<1.0) ans = i;    }    printf("Case #%d: %d\n", ++kas, ans);}int main(){    int _; scanf("%d", &_);    while(_--) solve();    return 0;}/*131:1.11:0.21.5:1.7*/

 

Problem F. Mr. Panda and Fantastic Beasts

n个字符串用分隔符连接起来s = s_1 + \$ + s_2 + \# + s3 + \# + \cdots + \# + s_n

s做后缀排序。使用height数组找到以s_1中元素开头的后缀和非s_1中元素开头的后缀的最大lcp,记作ans

min(ans)+1即为答案子串的长度,后缀数组保证了字典序最小,对应位置输出子串即可。如果对应min(ans)+1长度的子串不存在,即为无解。

#include<bits/stdc++.h>using namespace std ;const int maxn = 5e5 + 10 ;const int base = 131 ;typedef unsigned long long ull ;#include <ext/pb_ds/assoc_container.hpp>using namespace __gnu_pbds ;const ull mod1 = 68861641 ;const ull mod2 = 91815541 ;const int kk = 1e9 ;struct Sa //如果是多个测例,记得清空 s{    int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;    int tmp[maxn << 1] , cnt[maxn] ;    char s[maxn] ;    void cal(int n , int m)    {       n ++ ;       for(int i = 0 ; i < n * 2 + 5 ; i ++)         rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间       for(int i = 0 ; i < m ; i ++)  cnt[i] = 0 ;       for(int i = 0 ; i < n ; i ++)  cnt[rk[i] = s[i]] ++ ;       for(int i = 1 ; i < m ; i ++)  cnt[i] += cnt[i - 1] ;       for(int i = 0 ; i < n ; i ++)  sa[-- cnt[rk[i]]] = i ;       for(int k = 1 ; k <= n ; k <<= 1)       {         int j = 0 ;         for(int i = 0 ; i < n ; i ++)         {           j = sa[i] - k ;           if(j < 0)  j += n ;           tmp[cnt[rk[j]] ++] = j ;         }         sa[tmp[cnt[0] = 0]] = j = 0 ;         for(int i = 1 ; i < n ; i ++)         {           if(rk[tmp[i]] != rk[tmp[i - 1]]           || rk[tmp[i] + k] != rk[tmp[i - 1] + k])             cnt[++ j] = i ;           sa[tmp[i]] = j ;         }         memcpy(rk , sa , n * sizeof(int)) ;         memcpy(sa , tmp , n * sizeof(int)) ;         if(j >= n - 1)  break ;       }       height[0] = 0 ;       for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)         while(~k && s[i] != s[sa[j - 1] + k])           height[j] = k -- , j = rk[sa[j] + 1] ;    }    char t[maxn] ;    int len ;    int c ;    void init()    {        int n ;        cin >> n ;        cin >> s ;        len = strlen(s) ;        c = len ;        s[len ++] = '$' ;        for(int i = 2 ; i <= n ; i ++)        {            cin >> t ;            int len2 = strlen(t) ;            for(int j = 0 ; j < len2 ; j ++)  s[len ++] = t[j] ;            s[len ++] = '#' ;        }        cal(len , 200) ;    }    int mx[maxn] ;    int l[maxn] ;    int r[maxn] ;    bool vis[maxn] ;    int id , ans ;    void solve()    {        ans = 1e9 ;        for(int i = 0 ; i < len ; i ++)  mx[i] = l[i] = r[i] = 1e9 ;        int now = 1e9 ;        bool flag = false ;              for(int i = 2 ; i <= len ; i ++)        {            if(sa[i - 1] > c)  now = height[i] , flag = true ;            else  now = min(now , height[i]) ;            if(flag)  l[sa[i]] = now ;        }        now = 1e9 ;        flag = false ;        for(int i = len - 1 ; i >= 1 ; i --)        {            if(sa[i + 1] > c)  now = height[i + 1] , flag = true ;            else now = min(now , height[i + 1]) ;            if(flag)  r[sa[i]] = now ;        }        for(int i = 0 ; i < len ; i ++)  mx[i] = max(l[i] , r[i]) ;        // for(int i = 1 ; i <= len ; i ++)  cout << i << ' ' << height[i] << '\n' ;        // for(int i = 1 ; i <= len ; i ++)  cout << i << ' ' << l[sa[i]] << ' ' << r[sa[i]] << '\n' ;        for(int i = 1 ; i <= len ; i ++)              if(sa[i] < c && sa[i] + mx[sa[i]] < c && mx[sa[i]] + 1 < ans)                ans = mx[sa[i]] + 1 , id = sa[i] ;        for(int i = 0 ; i < len ; i ++)  mx[i] = l[i] = r[i] = 1e9 ;    }    void print()    {        if(ans > 1e8)  cout << "Impossible\n" ;        else        {            for(int i = id ; i <= id + ans - 1 ; i ++)  cout << s[i] ;            cout << '\n' ;        }    }} sa ;int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ": " ;        sa.init() ;        sa.solve() ;        sa.print() ;        //for(int i = 0 ; i < len ; i ++)  sa.s[i] = 0 ;    }    return 0 ;}

 

Problem G. Pandaria

考虑kruskal的过程,在添加边(u,v,w)时,新建一个虚点t,点权赋为wu,v所在连通块的祖先的父亲都是t。 

在每次询问x,w时,x向上倍增找到点权不超过w的祖先y,对y的子树进行众数查询。

众数查询可以用线段树合并、树上启发式合并。

我写了200行,是jyf的两倍多。谁的码力强已经很显然了。

 

 

我写的树上启发式合并。

#include<bits/stdc++.h>using namespace std ;typedef long long ll ;const int maxn = 2e5 + 10 ;const int maxm = 1e6 + 10 ;int n , m , q , c[maxn] ;int d[maxn] ;int fa[maxn][25] ;struct Link{    int num , head[maxn] ;    struct Edge    {        int v , next ;    } edge[maxn << 1] ;    void init()    {        num = 0 ;        memset(head , -1 , sizeof(head)) ;    }    void add_edge(int u , int v)    {        edge[num].v = v ;        edge[num].next = head[u] ;        head[u] = num ++ ;      }     void dfs1(int f , int u)    {       for(int i = 1 ; i <= 20 ; i ++)       {         int nxt = fa[u][i - 1] ;         fa[u][i] = fa[nxt][i - 1] ;       }       for(int i = head[u] ; i != -1 ; i = edge[i].next)       {         int v = edge[i].v ;         if(v == f)  continue ;         fa[v][0] = u ;         dfs1(u , v) ;       }    }} link ;struct Dsu_on_tree{    int siz[maxn] , son[maxn] ;    int flag ;    int ans[maxn] , cnt[maxm] ;    int res , max1 ;    void init()     {        res = 1e9 ;        max1 = flag = 0 ;        memset(siz , 0 , sizeof(siz)) ;        memset(son , 0 , sizeof(son)) ;        memset(ans , 0 , sizeof(ans)) ;        memset(cnt , 0 , sizeof(cnt)) ;       }    void dfs1(int f , int u)    {        siz[u] = 1 ;         for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)        {            int v = link.edge[i].v ;            if(v == f) continue ;            dfs1(u , v) ;             siz[u] += siz[v] ;            if(siz[v] > siz[son[u]]) son[u] = v ;        }    }    void add(int f , int u , int x)    {        cnt[c[u]] += x ;        if(x > 0 && c[u] > 0 && cnt[c[u]] >= max1)        {            if(cnt[c[u]] > max1)  res = c[u] , max1 = cnt[c[u]] ;            else  res = min(res , c[u]) ;                 }        for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)        {            int v = link.edge[i].v ;            if(v == f || v == flag) continue ;            add(u , v , x) ;        }    }    void dfs2(int f , int u , int keep)    {        for(int i = link.head[u] ; i != -1 ; i = link.edge[i].next)        {            int v = link.edge[i].v ;            if(v == f || v == son[u]) continue ;            dfs2(u , v , 0) ;        }        if(son[u])  dfs2(u , son[u] , 1) , flag = son[u] ;        add(f , u , 1) ;        ans[u] = res ;        if(son[u]) flag = 0 ;        if(!keep)  add(f , u , -1) , res = 1e9 , max1 = 0 ;    }    int cal(int x , int w)    {        // for(int i = 1 ; i <= n ; i ++)        // {        //     for(int j = 0 ; j <= 2 ; j ++)        //         cout << i << ' ' << j << ' ' << fa[i][j] << '\n' ;        // }        for(int i = 20 ; i >= 0 ; i --)        {            if(fa[x][i] > 0 && d[fa[x][i]] <= w)                x = fa[x][i] ;        }        return ans[x] ;    }} dsu_on_tree ;struct Dsu{    int pre[maxn] ;    void init(int n)    {        for(int i = 1 ; i <= n ; i ++)  pre[i] = i ;    }    int find(int u)     {        if(pre[u] == u)  return u ;        return pre[u] = find(pre[u]) ;    }    void add(int u , int v , int w)    {        int fu = find(u) ;        int fv = find(v) ;        if(fu != fv)        {            n ++ ;            d[n] = w ;            pre[fu] = n ;            pre[fv] = n ;            link.add_edge(n , fv) , link.add_edge(fv , n) ;            link.add_edge(n , fu) , link.add_edge(fu , n) ;            fa[fv][0] = n ;            fa[fu][0] = n ;            //cout << n << ' ' << fv << ' ' << w << '\n' ;            //cout << n << ' ' << fu << ' ' << w << '\n' ;            //if(fu == 5 || fv == 5)  cout << "$$$ " << n << ' ' << fa[5][0] << '\n' ;        }    }} dsu ;void init(){    cin >> n >> m ;    memset(c , 0 , sizeof(c)) ;    memset(d , 0 , sizeof(d)) ;    memset(fa , 0 , sizeof(fa)) ;    for(int i = 1 ; i <= n ; i ++)  cin >> c[i] ;    vector<array<int , 3>> g ;    dsu.init(n * 2) ;    for(int i = 1 ; i <= m ; i ++)    {        int u , v , w ;        cin >> u >> v >> w ;        g.push_back({w , u , v}) ;    }    sort(g.begin() , g.end()) ;    link.init() ;    for(auto now : g)    {        int u = now[1] ;        int v = now[2] ;        int w = now[0] ;        dsu.add(u , v , w) ;    }}void solve(){    link.dfs1(n , n) ;    dsu_on_tree.init() ;    dsu_on_tree.dfs1(n , n) ;     dsu_on_tree.dfs2(n , n , 0) ;    int lst = 0 ;    int q ;    cin >> q ;    while(q --)    {        int x , w ;        cin >> x >> w ;        x ^= lst , w ^= lst ;        int res = dsu_on_tree.cal(x , w) ;        cout << res << '\n' ;        lst = res ;    }}int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ":\n" ;        init() ;        solve() ;    }    return 0;}

 

jyf写的线段树合并。

#include <bits/stdc++.h>using namespace std;using pii = pair<int, int>;const int N = 1e5 + 5, LOG = 18;int kas;int n, m, q, c[N];vector<int> G[N<<1];struct edge{    int u, v, w;    bool operator < (const edge& oth) { return w < oth.w; }}e[N<<1];int fa[N<<1], val[N<<1], tot;int anc[N<<1][20];pii ans[N<<1];map<int, int> cnt[N<<1];int find(int x) { return x==fa[x] ? x : fa[x] = find(fa[x]); }void build(){    for(int i=1; i<=2*n; i++) fa[i] = i, G[i].clear();    sort(e+1, e+m+1);    tot = 0;    for(int i=1; tot<n-1; i++)    {        int u = find(e[i].u), v = find(e[i].v);        if(u!=v)        {            ++tot;            fa[u] = fa[v] = n+tot;            val[n+tot] = e[i].w;            G[n+tot].push_back(u), G[n+tot].push_back(v);        }    }}inline void upd(pii &x, pii y){    if(x.first<y.first) x = y;    else if(x.first==y.first&&x.second>y.second) x.second = y.second; }void merge(int u, int v){    if(cnt[u].size()<cnt[v].size()) swap(cnt[u], cnt[v]);    upd(ans[u], ans[v]);    for(auto it : cnt[v])    {        cnt[u][it.first] += it.second;        upd(ans[u], {cnt[u][it.first], it.first});    }}void dfs(int u){    for(int i=1; i<=LOG; i++) anc[u][i] = anc[anc[u][i-1]][i-1];    cnt[u].clear();    if(u<=n) cnt[u][c[u]]++, ans[u] = {1, c[u]};    else ans[u] = {0, 0};    for(int v : G[u])    {        anc[v][0] = u;        dfs(v);        merge(u, v);    }} void go(int &u, int w){    for(int i=LOG; ~i; i--)        if(anc[u][i] && val[anc[u][i]]<=w) u = anc[u][i];}void solve(){    scanf("%d%d", &n, &m);    for(int i=1; i<=n; i++) scanf("%d", c+i);    for(int i=1; i<=m; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);    build();    dfs(n+tot);    scanf("%d", &q);    int lst = 0;    printf("Case #%d:\n", ++kas);    while(q--)    {        int u, w; scanf("%d%d", &u, &w);        u ^= lst, w ^= lst;        go(u, w);        printf("%d\n", lst=ans[u].second);    }}int main(){    int _; scanf("%d", &_);    while(_--) solve();    return 0;}

 

Problem H. Great Cells

如果直接推A_g是什么容易把人推没了。

\sum (g+1) \cdot A_g = \sum g \cdot A_g + \sum A_g

容易发现\sum A_g = k^{n*m}

其次\sum g \cdot A_g相当于把$g$个极大值独立开,分别计算贡献。

因此ans=k^{n*m}+n*m*\sum_{i=1}^{k} (i-1)^{n-1+m-1}

#include<bits/stdc++.h>using namespace std ;const int mod = 1e9 + 7 ;typedef long long ll ;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 ;}int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ": " ;        int n , m , k ;        cin >> n >> m >> k ;        ll ans = 0 ;        ans += qpow(k , n * m) ; //g = 0        for(int i = 1 ; i <= k ; i ++)  //g >= 1            ans += n * m * qpow(i - 1 , n - 1 + m - 1) % mod * qpow(k , n * m - n - m + 1) % mod , ans %= mod ;        cout << ans << '\n' ;    }    return 0 ;}

 

Problem I. Cherry Pick

留坑。

 

Problem J. Mr.Panda and TubeMaster

留坑。

 

Problem K. Justice Rains From Above

留坑。

 

Problem L. World Cup

温暖的签到题。

#include<bits/stdc++.h>using namespace std ;int ans[15][15][15][15] ;int f[15] ;void dfs(int now){    if(now > 6)    {        int a = 0 ;        int b = 0 ;        int c = 0 ;        int d = 0 ;        if(f[1] == 2) //a win b        {            a += 3 ;        }        else if(f[1] == 1) // a tie b        {            a ++ ;            b ++ ;        }        else //a lose b        {            b += 3 ;        }        if(f[2] == 2) //a win c        {            a += 3 ;        }        else if(f[2] == 1) // a tie c        {            a ++ ;            c ++ ;        }        else //a lose c        {            c += 3 ;        }        if(f[3] == 2) //a win d        {            a += 3 ;        }        else if(f[3] == 1) // a tie d        {            a ++ ;            d ++ ;        }        else //a lose d        {            d += 3 ;        }        if(f[4] == 2) //b win c        {            b += 3 ;        }        else if(f[4] == 1) // b tie c        {            b ++ ;            c ++ ;        }        else //b lose c        {            c += 3 ;        }        if(f[5] == 2) //b win d        {            b += 3 ;        }        else if(f[5] == 1) // b tie d        {            b ++ ;            d ++ ;        }        else //b lose d        {            d += 3 ;        }        if(f[6] == 2) //c win d        {            c += 3 ;        }        else if(f[6] == 1) // c tie d        {            c ++ ;            d ++ ;        }        else //c lose d        {            d += 3 ;        }        ans[a][b][c][d] ++ ;        return ;    }    for(int i = 0 ; i < 3 ; i ++)  f[now] = i , dfs(now + 1) ;}void init(){    dfs(1) ;}int main(){    std::ios::sync_with_stdio(false) , cin.tie(0) ;    init() ;    int T ;    cin >> T ;    for(int cas = 1 ; cas <= T ; cas ++)    {        cout << "Case #" << cas << ": " ;        int a , b , c , d ;        cin >> a >> b >> c >> d ;        if(max({a , b , c , d}) > 9)  cout << "Wrong Scoreboard\n" ;        else        {            if(ans[a][b][c][d] == 1)  cout << "Yes\n" ;            else if(ans[a][b][c][d] > 1) cout << "No\n" ;            else  cout << "Wrong Scoreboard\n" ;        }    }    return 0 ;}

 

 

 

 

 

上一篇:Codeforces Round #703 (Div. 2) 题解
下一篇:炼金术士(2020-2021)

发表评论

最新留言

感谢大佬
[***.8.128.20]2025年04月10日 09时36分26秒