小言_互联网的博客

2018年第十届ACM四川省省赛题解(10 / 11)

494人阅读  评论(0)

2018ACM四川省省赛

题目链接:https://www.oj.swust.edu.cn/problem/search?word=The%202018%20Sichuan%20Provincial%20Collegiate%20Programming%20Contest&scope=source


5小时训练赛过9题

然后上周刚刚结束的2021年四川省赛同样9题拿金了,好耶,差两名拿季军奖杯Orz

A. Angel Beats

Problem

Solution

Code

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e4+2;
bitset<maxn>dp[maxn],sum[maxn],ans;
 
void solve()
{
   
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n+1;++i){
   
        sum[i].reset();
        dp[i].reset();
    }
    for(int i = n;i >= 1;--i){
   
        dp[i].set(i);
        for(int j = i+i;j <= n;j += i){
   
            dp[i] ^= dp[j];
        }
        sum[i] = dp[i]^sum[i+1];
    }
    ans.reset();
    for(int i = 1,a,b;i <= m;++i){
   
        scanf("%d%d",&a,&b);
        ans ^= sum[a]^sum[b+1];
        printf("%d\n",ans.count());
    }
}
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        solve();
    }
}

B. Beyond the Boundry

Problem

Solution

暴力即可。

Code

#include <bits/stdc++.h>
using namespace std;
 
string ss[4] = {
   "Kanbara Akihito", "Kuriyama Mirai","Nase Hiroomi","Nase Mitsuki"};
 
bool check(string a, string b)
{
   
    int i = 0;
    int j = 0;
    while(i < (int)a.size() && j < (int)b.size()){
   
        if(b[j] == a[i]){
   
            i++;
        }
        j++;
    }
    return i == (int)a.size();
 
}
 
int main()
{
      
    int t;
    scanf("%d",&t);
    while(t--){
   
        string s;
        cin>>s;
        vector<string> tmp;
        for(int i = 0;i < 4;++i){
   
            if(check(s,ss[i])) tmp.push_back(ss[i]);
        }
        printf("%d\n",(int)tmp.size());
        for(auto it : tmp) cout << it << endl;
    }
 
 
    return 0;
}

C. Clannad

Problem

Solution

AC自动机上DP即可。

Code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int mod = 1e9+7;
const int maxn = 1e5+5;
 
struct ACAM
{
   
    int tree[maxn][26],fail[maxn],last[maxn],tot,end[maxn],len[maxn];
    ll dp[maxn];
    void init()
    {
   
        memset(dp,0,sizeof(dp));
        dp[0] = 1;
        tot = 0;
        memset(tree,0,sizeof(tree));
        memset(fail,0,sizeof(fail));
        memset(last,0,sizeof(last));
        memset(end,0,sizeof(end));
        memset(len,0,sizeof(len));
    }
    void Insert(const char *s){
   
        int root = 0;
        int l = 0;
        for(int i = 0;s[i];++i){
   
            int p = s[i]-'a';
            l++;
            if(!tree[root][p]){
   
                tree[root][p] = ++tot;
                len[tot] = l;
            }
            root = tree[root][p];
        }
        end[root] = 1;
    }
    void get_fail(){
   
        queue<int> q;
        for(int i = 0;i < 26;++i){
   
            if(tree[0][i])
            q.push(tree[0][i]); 
        }
        while(!q.empty()){
   
            int now = q.front();
            q.pop();
            last[now] = end[fail[now]]?fail[now]:last[fail[now]];
            for(int i = 0;i < 26;++i){
   
                if(tree[now][i]){
   
                    q.push(tree[now][i]);
                    fail[tree[now][i]] = tree[fail[now]][i];
                }
                else tree[now][i] = tree[fail[now]][i];
            }
        }
    }
    void solve(const char*s,int n){
   
        int root = 0;
        for(int i = 1;i <= n;++i){
   
            root = tree[root][s[i]-'a'];
            int tmp = root;
            if(end[root]){
   
                dp[i] += dp[i-len[root]];
                dp[i] %= mod;
            }
            tmp = last[tmp];
            while(tmp){
   
                dp[i] += dp[i-len[tmp]];
                dp[i] %= mod;
                tmp = last[tmp];
            }
            printf("%lld%c",dp[i],i==n?'\n':' ');
        }
    }
}ac;
char s[maxn],t[maxn];
 
void solve()
{
   
    ac.init();
    int n,q;
    scanf("%d%d",&n,&q);
    scanf("%s",s+1);
    for(int i = 1;i <= q;++i){
   
        scanf("%s",t);
        ac.Insert(t);
    }
    ac.get_fail();
    ac.solve(s,n);
}
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
     
        solve();
    }
}
 
// 9 5
// zzbzzbcbb
// zz
// z
// zb
// cb
// b

D. Darling in the Franxx

Problem


Solution

首先以 1 为根进行 dfs ,记录一下祖先信息,dfs序。对于任意两个点若他们不是祖先关系则只有当他们的子树上的点为根时才能满足祖先关系。若是祖先关系,则他们路径上的点及其所有子节点都不能作为根。因为一个点的子树上的点的dfs序都是连续的所以对子树上的点打标记用差分即可。

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int LGS = 18;
vector<int> edge[maxn];
int dfn[maxn],rnk[maxn],dfn_clock;
int l[maxn],r[maxn];
int son[maxn],dep[maxn];
int fa[maxn][20];
int val[maxn];
 
void dfs(int now,int pre)
{
   
    l[now] = ++dfn_clock;
    dep[now] = dep[pre]+1;
    fa[now][0] = pre;
    for(int i = 1;i <= LGS;++i){
   
        fa[now][i] = fa[fa[now][i-1]][i-1];
    }
    for(auto &x:edge[now]){
   
        if(x==pre)continue;
        dfs(x,now);
    }
    r[now] = dfn_clock;
}
int lca(int x,int y)
{
   
    if(dep[x] < dep[y])swap(x,y);
    for(int i = LGS;i >= 0;--i){
   
        if(dep[fa[x][i]] >= y)x = fa[x][i];
    }
    for(int i = LGS;i >= 0;--i){
   
        if(fa[x][i]!=fa[y][i]){
   
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    if(x!=y)return fa[x][0];
    return x;
}
 
void init()
{
   
    dfn_clock = 0;
    memset(son,0,sizeof(son));
    memset(val,0,sizeof(val));
}
 
void solve()
{
   
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;++i)edge[i].clear();
    init();
    for(int i = 1,a,b;i < n;++i){
   
        scanf("%d%d",&a,&b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    dfs(1,0);
    for(int i = 1,a,b;i <= m;++i){
   
        scanf("%d%d",&a,&b);
        int g = lca(a,b);
        if(dep[a] > dep[b])swap(a,b);
        if(g==a){
   
            int x = b;
            for(int i = LGS;i >= 0;--i){
   
                if(dep[fa[x][i]]>dep[g])x = fa[x][i];
            }
            val[1]++;
            val[l[x]]--,val[r[x]+1]++;
            val[l[b]]++,val[r[b]+1]--;
        }
        else{
   
            val[l[a]]++,val[r[a]+1]--;
            val[l[b]]++,val[r[b]+1]--;
        }
    }
    int ans = 0;
    for(int i = 1;i <= n;++i){
   
        val[i] += val[i-1];
        ans += val[i]==m;
    }
    printf("%d\n",ans);
}
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        solve();
    }
    return 0;
}

E. Ever17

Problem

Solution

模拟即可,注意 01/01/01 和 01/01/01 是同一天,所以要输出 January 1, 2001,因为是同一天。

Code

#include <bits/stdc++.h>
using namespace std;
 
string s[13] = {
   "","January","February","March","April","May","June","July","August","September","October","November","December"};
 
int day[13] = {
   0,31,28,31,30,31,30,31,31,30,31,30,31};
int addone(int y)
{
   
    if(y % 4 == 0) return 1;
    return 0;
}
 
bool check1(int a,int b,int c)
{
   
    if(a > 12 || a < 1) return 0;
    if(b < 1 || b > day[a] + (a == 2 ? addone(c) : 0)) return 0;
    return 1;
}
 
 
int calc(vector<int> a, vector<int> b)
{
   
    if(a > b) swap(a,b);
    int cnt = 0;
    while(a != b){
   
        a[2]++;
        if(a[2] > day[a[1]] + (a[1] == 2 ? addone(a[0]) : 0) ) a[2] = 1,a[1]++;
        if(a[1] == 13) a[1] = 1,a[0]++;
        cnt++;
 
    }
    return cnt;
}
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        int a,b,c;
        scanf("%d/%d/%d",&a,&b,&c);
        vector<int> x,y;
        if(check1(a,b,c)) x.push_back(c),x.push_back(a),x.push_back(b);
        if(check1(b,c,a)) y.push_back(a),y.push_back(b),y.push_back(c);
        if(x.size() && y.size() && x != y){
   
            int cnt = calc(x,y);
            printf("%d\n",cnt);
            continue;
        }
        if(x.size()){
   
            cout<<s[x[1]];
            printf(" %d, 20%02d\n",x[2],x[0]);
            continue;
        }
        if(y.size()){
   
            cout<<s[y[1]];
            printf(" %d, 20%02d\n",y[2],y[0]);
            continue;
        }
    } 
    return 0;
}

F. Fullmetal Alchemist

Problem

Solution

考虑 O ( n 2 ) O(n^2) O(n2) 枚举子区间,枚举的同时维护对顶堆,复杂度 O ( T ∗ n 2 ∗ l o g n ) O(T*n^2*logn) O(Tn2logn)

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2005;
using ll = long long;
int a[maxn];
ll sum[maxn];
ll ans[maxn];
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        int n;
        memset(ans,0,sizeof(ans));
        scanf("%d",&n);
        for(int i = 1;i <= n;++i){
   
            scanf("%d",&a[i]);
            sum[i] = sum[i-1]+a[i]; 
        }
        for(int i = 1;i <= n;++i){
   
            priority_queue<int,vector<int>,greater<int>> mx;
            priority_queue<int,vector<int>,less<int>> mi;
            bool now = 1;
            ll add = 0;
            for(int j = i;j <= n;++j){
   
                if(now){
   
                    if(mi.empty())mi.push(a[j]),add+=a[j];
                    else{
   
                        int tmp = mx.top();
                        if(a[j] > tmp){
   
                            mx.pop();
                            mx.push(a[j]);
                            mi.push(tmp);
                            add += tmp;
                        }
                        else {
   
                            mi.push(a[j]);
                            add += a[j];
                        }
                    }
                }
                else{
   
                    int tmp = mi.top();
                    if(a[j] < tmp){
   
                        mi.pop();
                        mi.push(a[j]);
                        mx.push(tmp);
                        add -= tmp-a[j];
                    }
                    else {
   
                        mx.push(a[j]);
                    }
                    ans[(j-i+1)/2] = max(ans[(j-i+1)/2],sum[j]-sum[i-1]-add-add);
                   // cout<<i<<' '<<j<<' '<<add<<' '<<sum[j]-sum[i-1]-add-add<<endl;
                }
                now = !now;
            }
        }
        for(int i = 1;i <= n/2;++i)printf("%lld%c",ans[i],(i==n/2?'\n':' '));
    }
    return 0;
}

G. Grisaia

Problem

计算:

a n s = ∑ i = 1 n ∑ j = 1 i ( n   m o d ( i × j ) ) ans =\sum^n_{i=1}\sum^i_{j=1} (n\ mod (i \times j)) ans=i=1nj=1i(n mod(i×j))

其中 t ≤ 5 , n ≤ 1 0 11 t\le 5, n\le 10^{11} t5,n1011

Solution

使用模的展开式将上述和式展开后,显然枚举 k = i × j k=i\times j k=i×j,由于 n ≤ 1 0 11 n\le10^{11} n1011,杜教筛即可。

筛出:

f ( x ) = x × ∑ i ∣ k 1 g ( x ) = x × μ ( x ) f(x)=x\times \sum\limits_{i|k} 1\\g(x)=x \times \mu(x) f(x)=x×ik1g(x)=x×μ(x)

然后整除分块即可。

详解见这里:https://blog.csdn.net/weixin_45697774/article/details/117172503

附训练赛时草稿纸一份:


这里是杜教筛筛出来 g ( x ) = x × μ ( x ) g(x)=x\times \mu(x) g(x)=x×μ(x)

注意 n ≤ 1 0 11 n\le10^{11} n1011,中间多处会爆 long long,强转成 __int128 即可。

(因为这个wa了8发hhh,五颜六色的)

Code

#include <bits/stdc++.h>
   
using namespace std;
#define int long long
#define ll __int128
const int N = 31644346;
   
int n, m;
int mu[N];
int primes[N], cnt;
int d[N];
int num[N];
unordered_map<int, ll> sum_mui;
unordered_map<int, ll> sum_dk;
bool vis[N];
int sum[N];
   
inline ll read()
{
   
    register ll x = 0,f = 1;
    char c = getchar();
    while(c < '0' || c > '9') {
   if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c-48,c = getchar();
    return x * f;
}
   
inline void print(ll x)
{
   
    if(x < 10)
    {
   
        putchar(x + 48);
        return;
    }
    print(x / 10), print(x % 10);
}
   
void init(int n)
{
   
    vis[0] = vis[1] = 1;
    mu[1] = d[1] = 1;
    for(int i = 2; i <= n; ++ i) {
   
        if(vis[i] == 0) {
   
            primes[ ++ cnt] = i;
            mu[i] = -1;
            d[i] = 2 * i;
            num[i] = 1;
        }
        for(int j = 1; j <= cnt && i * primes[j] <= n; ++ j) {
   
            vis[i * primes[j]] = 1;
            if(i % primes[j] == 0) {
   
                mu[i * primes[j]] = 0;
                num[i * primes[j]] = num[i] + 1;
                d[i * primes[j]] = (ll)d[i] / num[i * primes[j]] * (num[i * primes[j]] + 1) * primes[j];
                break;
            }
            mu[i * primes[j]] -= mu[i];
            num[i * primes[j]] = 1;
            d[i * primes[j]] = d[i] * d[primes[j]];
        }
    }
    for(int i = 1; i <= n; ++ i) {
   
        sum[i] = sum[i - 1] + mu[i] * i;
        d[i] = d[i - 1] + d[i];
    }
}
   
inline ll get_sum_mui(int x)
{
   
    if(x <= N - 7) return sum[x];
    if(sum_mui.find(x) != sum_mui.end()) return sum_mui[x];
       
    ll ans = 1;
    for(ll l = 2, r; l <= x; l = r + 1) {
   
        r = x / (x / l);
        ans -= (ll)(r - l + 1) * (l + r) / 2 * get_sum_mui(x / l);
    }   
    return sum_mui[x] = ans;
}
   
inline ll get_sum_dk(ll x)
{
   
    if(x <= N - 7) return d[x];
    if(sum_dk.find(x) != sum_dk.end()) return sum_dk[x];
    ll ans = x * (x + 1) / 2;
    for(ll l = 2, r; l <= x; l = r + 1) {
   
        r = x / (x / l);
        ans -= (ll)(get_sum_mui(r) - get_sum_mui(l - 1)) * get_sum_dk(x / l);
    }
    return sum_dk[x] = ans;
}
   
ll cal(ll x)
{
   
    ll limit = sqrt(x + 0.99);
    ll more = limit * (limit + 1) * (2 * limit + 1) / 6;
    return (get_sum_dk(x) + more) / 2;
}
   
void solve()
{
   
    ll ans = (ll)n * n * (n + 1) / 2;
    for(ll l = 1, r; l <= n; l = r + 1) {
   
        r = n / (n / l);
        //cout << "ok" << cal(r) - cal(l - 1) * (n / l) << endl;
        ans -= (ll)(cal(r) - cal(l - 1)) * (n / l);
    }
    print(ans);
    puts("");
}
   
signed main()
{
   
    int t;
    init(N - 7);
    t = read();
    while(t -- ) {
   
        n = read();
        solve();
    }
    return 0;
} 

H. Harmony

Problem

给你 a , b a,b a,b,你自己选一个 c c c,使得 a + b + gcd ⁡ ( a , b , c ) a + b + \gcd(a, b, c) a+b+gcd(a,b,c) 最大,输出这个最大的 a + b + gcd ⁡ ( a , b , c ) a + b + \gcd(a, b, c) a+b+gcd(a,b,c)

Solution

显然 c = gcd ⁡ ( a , b ) c=\gcd(a,b) c=gcd(a,b) 时, a + b + gcd ⁡ ( a , b , c ) a + b + \gcd(a, b, c) a+b+gcd(a,b,c) 最大

Code

#include <bits/stdc++.h>
using namespace std;
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d\n",a + b + __gcd(a,b));
    } 
    return 0;
}

I. Island

Problem

Solution

根据题意直接 dfs 即可

Code

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 2e5+5;
 
ll dp[maxn];
ll sum = 0;
ll val[maxn];
vector<int> edge[maxn];
ll ans;
 
void dfs(int now,int pre)
{
   
    dp[now] = val[now];
    ll mx = 0;
    for(auto &x:edge[now]){
   
        if(x==pre)continue;
        dfs(x,now);
        dp[now] += dp[x];
        mx = max(mx,dp[x]);
    }
    mx = max(mx,sum-dp[now]);
    ans = min(ans,mx);
}
 
void solve()
{
   
     
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;++i)edge[i].clear();
    sum = 0;
    for(int i = 1;i <= n;++i)scanf("%lld",&val[i]),sum += val[i];
    for(int i = 1,a,b;i < n;++i){
   
        scanf("%d%d",&a,&b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    ans = 1e18;
    dfs(1,0);
    printf("%lld\n",ans);
}
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        solve();
    }
    return 0;
}

J. JoJo’s Bizarre Adventure

Problem

Translation

jojo 有一个长度为 n n n a a a 数组,其中保证 a 0 = 0 a_0=0 a0=0,且 0 = a 1 ≤ a 2 ≤ ⋅ ⋅ ⋅ ≤ a n 0 = a_1 ≤ a_2 ≤ · · · ≤ a_n 0=a1a2an,jojo 构造出了一个多重集: S m u l t i = { a i + a j ∣ 1 ≤ i < j ≤ n } S_{multi} = \{a_i+a_j |1 ≤ i < j ≤ n\} Smulti={ ai+aj1i<jn},其中保证 S m u l t i S_{multi} Smulti 非严格单调递增。

Dio 想要最少需要知道多少个 S m u l t i S_{multi} Smulti 里的元素,就可以推断出原序列 a a a

T ≤ 10 , n ≤ 500 , S m u l t i . s i z e = n ( n − 1 ) 2 T\le 10,n\le 500,S_{multi}.size=\cfrac{n(n-1)}{2} T10,n500,Smulti.size=2n(n1)

Solution

题太难理解了…
读懂题之后直接模拟即可…
注意特判…

Code

#include <bits/stdc++.h>
using namespace std;
 
const int N = 3e5 + 10;
int a[N];
unordered_map<int,int> mp;
 
int main()
{
   
    int t;
    scanf("%d",&t);
    while(t--){
   
        mp.clear();
        int n;
        scanf("%d",&n);
        vector<int> res;
        int m = n * (n-1) / 2;
        for(int i = 1;i <= m;++i){
   
            scanf("%d",&a[i]);
        }
        if(n == 1){
   
            puts("0");
            continue;
        }
        if(n == 2){
   
            puts("1");
            continue;
        }
        if(n == 3){
   
            puts("2");
            continue;
        }
        res.push_back(0);
        res.push_back(a[1]);
        res.push_back(a[2]);
        // mp[a[1]] = 0, mp[a[2]] = 1;
        mp[a[1] + a[2]] = 1;
        int cnt = 2;
        for(int i = 3;i <= m;++i){
   
            if(mp[a[i]]) {
   
                mp[a[i]]--;
                continue;
            }
            for(int j = 1;j < res.size();++j) {
   
                mp[a[i] + res[j]]++;
            }
            res.push_back(a[i]);
            if((int)res.size() == n){
   
                cnt = i;
                break;
            }
        }
        printf("%d\n",cnt);
    }
 
 
    return 0;
}

K. Kanon

Problem

Solution

Code


转载:https://blog.csdn.net/weixin_45697774/article/details/117089776
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场