Dancepted

Dancing Acceped!

模拟赛小结:Gym 102800 The 14th Jilin Provincial Collegiate Programming Contest

比赛链接:传送门

阴差阳错地又开始训练了。

lh考研去了,我和hat又坑到了另一个很强的学弟guapi组队。The 1226465-th prime number 又回来啦。

我和guapi差不多停训了一年,hat倒是一直在训练,前段时间cf还上了橙,Orz。

 

回来第一场先练了个吉林的省赛热热手(省赛的水题还是挺多的,做得很开心),罚时放到现场赛居然还能拿个亚军。


 

Problem A. Chord 00:15 (+) Solved by xk (小模拟)

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int main()
{
    map<string, int> mp;
    mp["C"] = 1;
    mp["C#"] = 2;
    mp["D"] = 3;
    mp["D#"] = 4;
    mp["E"] = 5;
    mp["F"] = 6;
    mp["F#"] = 7;
    mp["G"] = 8;
    mp["G#"] = 9;
    mp["A"] = 10;
    mp["A#"] = 11;
    mp["B"] = 12;
    fast;
    int T;
    cin >> T;
    while(T--)
    {
        string a, b, c;
        cin >> a >> b >> c;
        int x = mp[a], y = mp[b], z = mp[c];
        if(x > y) y += 12;
        if(y > z) z += 12;
        if(y - x == 4 && z - y == 3) cout << "Major triad\n";
        else if(y - x == 3 && z - y == 4) cout << "Minor triad\n";
        else cout << "Dissonance\n";
    }
}
View Code

 

Problem B. Problem Select 00:06 (+) Solved by Guapi (排序 签到)

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, k, a[N];
char s[N];
int main()
{
    int t;
    scanf("%d", &t);
    while(t --)
    {
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; ++ i) {
            scanf("%s", s + 1);
            int len = strlen(s + 1);
            int j = len;
            while(j > 0 && s[j] != '/') -- j;
            int x = 0;
            for(j = j + 1; j <= len; ++ j) {
                x = x * 10 + s[j] -'0';
            }
            a[i] = x;
        }
        sort(a + 1, a + n + 1);
        for(int i = 1; i <= k; ++ i) {
            printf("%d ", a[i]);
        }
        puts("");
    }
    return 0;
}
View Code

 

Problem C. String Game 00:32 (+1) Solved by Dancepted (dp 背包)

题目大意:

  给两个字符串s,t,求字符串s有多少个子序列和t完全相同。

解法:

  用$cnt_{j}$表示t的长度为j的前缀能有多少个匹配。那么在后面出现$s_{i}=t_{j+1}$时,长度为j+1的前缀就会增加cnt_{j}个匹配。

  是一个类似01背包的转移,所以第二个循环应该倒着跑。

代码:$O(nm)$

#include <bits/stdc++.h>
#define sz(x) ((int)x.size())
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
#define N 5005
#define M 1005

using namespace std;
typedef long long ll;

ll cnt[M];
ll md = 1000000007;
int main(){
    fast;
    string s, t;
    while (cin >> s >> t) {
        memset(cnt, 0, (sz(t) + 1) * sizeof(ll));
        cnt[0] = 1;
        for (int i = 0; i < sz(s); i++) {
            for (int j = sz(t)-1; j >= 0; j--) {
                if (s[i] == t[j]) {
                    cnt[j+1] = (cnt[j+1] + cnt[j]) % md;
                }
            }
        }
        cout << cnt[sz(t)] << endl;
    }
    return 0;
}
View Code

 

Problem D. Trie (待补)

  hat的补题代码(做法大概是AC自动机fail树上用树状数组搞一下差分什么的)

#include<bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < (n); i++)
#define forab(i, a, b) for (int i = (a); i <= (b); i++)
#define forba(i, b, a) for (int i = (b); i >= (a); i--)
#define mset(a, x, n) memset(a, x, sizeof(a[0]) * (n))
#define updiv(x, y) (((x) + (y) - 1) / (y)) // y > 0
#define pb(x) push_back(x)
#define eb emplace_back
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
#define sz(x) ((int)x.size())
#define all(x) (x).begin(), (x).end()
#define endl '\n'
#ifdef hat
#define fast
#define de(x) cout  << #x << '=' << (x) << ' '
#define dee(x) cout  << #x << '=' << (x) << endl
#else
#define fast ios::sync_with_stdio(0), cin.tie(0)
#define de(x) ((void) 0)
#define dee(x) ((void) 0)
#endif
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair<int, int> pii;
typedef vector<int> VI;

const int maxn = 1e5 + 5;
const int mod = 998244353;
const int INF = 0x3f3f3f3f;
const ll llINF = 0x3f3f3f3f3f3f3f3f;
ll make_compiler_happy() {return INF & maxn & mod & llINF;}
ll fpow(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;}
ll inv(ll x) {return fpow(x, mod-2);}

int ch[maxn][26];
int n;
int f[maxn];
vector<int> g[maxn];

void getfail()
{
    queue<int> q;
    f[0] = 0;
    for(int c = 0; c < 26; c++)
    {
        int u = ch[0][c];
        if(u) {
            f[u] = 0;
            q.push(u);
        }
    }
    while(!q.empty())
    {
        int r = q.front(); q.pop();
        for(int c = 0; c < 26; c++)
        {
            int u = ch[r][c];
            if(!u) continue;
            q.push(u);
            int v = f[r];
            while(v && !ch[v][c]) v = f[v];
            f[u] = ch[v][c];
        }
    }
    for(int i = 1; i < n; i++)
    {
        g[f[i]].push_back(i);
    }
}

int cur;
int in[maxn], out[maxn];
int dep[maxn];

void dfs1(int u, int d)
{
    dep[u] = d;
    in[u] = ++cur;
    for(int v : g[u])
    {
        dfs1(v, d + 1);
    }
    out[u] = cur;
}

int sum[maxn * 4];
#define mid (l+r)/2
#define ls o*2
#define rs o*2+1

void add(int o, int l, int r, int ql, int qr)
{
    if(ql <= l && r <= qr) {
        sum[o]++;
        return;
    }
    if(ql > r || qr < l) {
        return;
    }
    add(ls, l, mid, ql, qr);
    add(rs, mid+1, r, ql, qr);
}

int bit[maxn];

void bitadd(int x, int d)
{
    for(; x <= cur + 1; x += x & -x) {
        bit[x] += d;
    }
}

int bitsum(int x)
{
    int s = 0;
    for(; x; x -= x & -x) {
        s += bit[x];
    }
    return s;
}

void add(int x)
{
    add(1, 1, cur, in[x], out[x]);
}

int query(int o, int l, int r, int x, int s)
{
    if(l == r) return s + sum[o];
    if(x <= mid) return query(ls, l, mid, x, s + sum[o]);
    else return query(rs, mid + 1, r, x, s + sum[o]);
}

int main() 
{
    fast;
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        memset(sum, 0, sizeof(sum));
        for(int i = 0; i < n; i++)
        {
            g[i].clear();
            memset(ch[i], 0, sizeof(ch[i]));
        }
        for(int i = 1; i < n; i++)
        {
            int u; char c;
            cin >> u >> c;
            u--;
            ch[u][c-'a'] = i;
        }
        
        cur = 0;
        getfail();
        dfs1(0, 0);
        int m;
        cin >> m;
        while(m--)
        {
            int t;
            cin >> t;
            if(t == 1) {
                int k;
                cin >> k;
                vector<int> v(k);
                for(int i = 0; i < k; i++)
                {
                    cin >> v[i];
                    v[i]--;
                }
                sort(v.begin(), v.end(), [&](int x, int y) {return dep[x] < dep[y];});
                vector<int> tmp;
                for(int i : v)
                {
                    if(bitsum(in[i])) {
                        continue;
                    }
                    add(i);
                    tmp.push_back(i);
                    bitadd(in[i], 1);
                    bitadd(out[i] + 1, -1);
                }
                for(int i : tmp)
                {
                    bitadd(in[i], -1);
                    bitadd(out[i] + 1, 1);
                }
                // forn(i, n)
                // {
                //     cout << query(1, 1, cur, in[i], 0) << ' ';
                // }
                // cout << endl;
            }
            else {
                int x;
                cin >> x;
                x--;
                cout << query(1, 1, cur, in[x], 0) << endl;
            }
        }
    }
}
View Code

 

Problem E. Shorten the Array 00:51 (+) Solved by Dancepted&hat (思维题)

  看题目发呆了好久,后来脑门一拍想了个结论就过了。

题目大意:

  给出一个长度为n(n <= 1e6)的数组,每次可以选择两个位置相邻的数a、b(要求a和b都大于0),用a%b或者b%a替换他们。

思路:
  设最小的数是mn,数量有cnt个。

  如果cnt = 1,显然可以通过(ai, mn)=(mn % ai)= (mn),来删去所有的点,答案就是1。

  如果cnt ≠ 1,则需要讨论一下。

两个结论:

  1、若所有的数都是mn的倍数,则答案为$\left \lceil \frac{cnt}{2} \right \rceil$

  2、若存在一个数x不是mn的倍数,设y为与mn相邻的一个数则必定可以通过(mn, y) = (mn % y),把mn换到x的旁边,然后(mn, x)=(x%mn),且x%mn < mn,之后就可以用x%mn消去所有的

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n;
int a[N];
void solveTestCase() {
    cin >> n;
    int mn = 1e9 + 5, cnt = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if (mn > a[i]) {
            mn = a[i];
            cnt = 1;
        }
        else if (mn == a[i]) {
            cnt++;
        }
    }
    bool isOne = false;
    for (int i = 1; i <= n; i++) {
        if (a[i] % mn != 0) {
            isOne = true;
            break;
        }
    }
    if (isOne)
        cout << 1 << endl;
    else
    {
        cout << (cnt + 1) / 2 << endl;
    }
    
}

int main() {
    fast;
    int t;
    cin >> t;
    while (t--) {
        solveTestCase();
    }
    return 0;
}
View Code

 

Problem F. Queue 04:11 (+5) Solved by Guapi (树状数组+暴力,树套树)

  没看到$p_{i2}−p_{i1} ≤ 100$,用树套树写了半天没调出来的样子,后来看到了这个条件就暴力过了。

  因为太久做题,出现了一些RE,MLE之类的错误,多了不少罚时。

题目大意:

  动态逆序对,每次操作是交换两个数的位置,或询问逆序对的数量。

解法:

  树套树经典题,但这个两个数的位置距离不超过100,也可以树状数组+暴力。

比赛代码(树状数组+暴力):

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N = 1e5 + 10;
const int M = 2500000;
int a[N], n, m;
int c[N];
void add(int x) {
    for(; x <= N - 5;  x += lowbit(x)) {
        c[x] ++ ;
    }
}
int ask(int x) {
    int res = 0;
    for(; x > 0; x -= lowbit(x)) {
        res += c[x];
    }
    return res;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        ll cnt = 0;
        scanf("%d", &n);
        memset(c, 0, sizeof(c));
        for(int i = 1; i <= n; ++ i) {
            scanf("%d", &a[i]);
            ++ a[i];
            cnt += i - 1 - ask(a[i]);
            add(a[i]);
        }
        ll ans = cnt;
        scanf("%d", &m);
        int L, R;
        while(m --)
        {
            scanf("%d%d", &L, &R);
            if(L == R) continue;
            int cnt1 = 0, cnt2 = 0;
            for(int j = L + 1; j <= R - 1; ++ j) {
                if(a[j] < a[L]) ++ cnt1;
                if(a[j] > a[R]) ++ cnt2;
            }
            cnt = cnt - cnt1 - cnt2;
            cnt1 = cnt2 = 0;
            for(int j = L + 1; j <= R - 1; ++ j) {
                if(a[j] > a[L]) ++ cnt1;
                if(a[j] < a[R]) ++ cnt2;
            }
            cnt = cnt + cnt1 + cnt2;
            if(a[L] > a[R]) -- cnt;
            swap(a[L], a[R]);
            if(a[L] > a[R]) ++ cnt;
            ans = min(ans, cnt);
        }
        printf("%lld\n", ans);
    }
    return 0;
}
View Code

树套树代码:

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N = 1e5 + 10;
const int M = 2500000;
int a[N], tot, n, m, mx;
struct tree{
    int lc, rc, cnt;
}T[N << 7];
int root[N];//正常树的根节点
int ux[50], uy[50], numx, numy; //保存树状数组的节点
int S[N];//树状数组根节点
void build(int &rt, int l, int r)//建树
{
    rt = ++ tot;
    T[rt].cnt = 0;
    T[rt].lc = T[rt].rc = 0;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(T[rt].lc, l, mid);
    build(T[rt].rc, mid + 1, r);
}
void UP(int &cur, int l, int r, int pos, int val)//节省内存
{
    if (!cur) cur = ++ tot;
    T[cur].cnt += val;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) UP(T[cur].lc, l, mid, pos, val);
    else UP(T[cur].rc, mid + 1, r, pos, val);
}
void update(int &cur, int pre, int l, int r, int pos, int val)
{
    cur = ++tot;//新树必须新开节点
    T[cur] = T[pre];
    T[cur].cnt += val;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) update(T[cur].lc, T[pre].lc, l, mid, pos, val);
    else update(T[cur].rc, T[pre].rc, mid + 1, r, pos, val);
}
void clear(int &cur, int l, int r)
{
    T[cur].cnt = 0;
    if(!cur || l == r) return ;
    int mid = (l + r) >> 1;
    if(T[cur].lc) clear(T[cur].lc, l, mid);
    if(T[cur].rc) clear(T[cur].rc, mid + 1, r);
    cur = 0;
}

void add(int pos, int val)//用树状数组更新
{
    int x = a[pos];
    for (; pos <= n; pos += lowbit(pos))
        UP(S[pos], 0, mx, x, val);
}

int query(int l, int r, int L, int R, int x, int y) {
    if(x > y) return 0;
    if (x <= l && r <= y)
    {
        int ans = T[R].cnt - T[L].cnt;
        for (int i = 0; i < numy; i++) ans += T[uy[i]].cnt;
        for (int i = 0; i < numx; i++) ans -= T[ux[i]].cnt;
        return ans;
    }
    int  Ux[50], Uy[50];
    for (int i = 0; i < numy; i++) Uy[i] = uy[i];
    for (int i = 0; i < numx; i++) Ux[i] = ux[i];
    int mid = (l + r) >> 1;
    int sum = 0;
    if (x <= mid)
    {
        for (int i = 0; i < numy; i++) uy[i] = T[Uy[i]].lc;
        for (int i = 0; i < numx; i++) ux[i] = T[Ux[i]].lc;
        sum = query(l, mid, T[L].lc, T[R].lc, x, y);
    }
    if (y > mid)
    {
        for (int i = 0; i < numy; i++) uy[i] = T[Uy[i]].rc;
        for (int i = 0; i < numx; i++) ux[i] = T[Ux[i]].rc;
        sum += query(mid + 1, r, T[L].rc, T[R].rc, x, y);
    }
    return sum;
}

int Q(int L)
{
    int cnt = 0;
    // left, large than a[L]
    if(L > 1) {
        numx = 0, numy = 0;
        for (int j = 0; j > 0; j -= lowbit(j)) ux[numx++] = S[j];
        for (int j = L - 1; j > 0; j -= lowbit(j)) uy[numy++] = S[j];
        cnt += query(0, mx, root[0], root[L - 1], a[L] + 1, mx);
    }
    // right, less than a[L]
    if(L < n) {
        numx = 0, numy = 0;
        for (int j = L; j > 0; j -= lowbit(j)) ux[numx++] = S[j];
        for (int j = n; j > 0; j -= lowbit(j)) uy[numy++] = S[j];
        cnt += query(0, mx, root[L], root[n], 0, a[L] - 1);
    }
    return cnt;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t --)
    {
        tot = 0;
        ll cnt = 0;
        scanf("%d", &n);
        mx = 0;
        for(int i = 1; i <= n; ++ i) {
            scanf("%d", &a[i]);
            mx = max(mx, a[i]);
        }
        build(root[0], 0, mx);
        numx = 0, numy = 0;
        for(int i = 1; i <= n; ++ i) {
            S[i] = 0;
            update(root[i], root[i - 1], 0, mx, a[i], 1);
            cnt += query(0, mx, root[0], root[i - 1], a[i] + 1, mx);
        }
        ll ans = cnt;
        int L, R;
        scanf("%d", &m);
        while(m --)
        {
            scanf("%d%d", &L, &R);
            if(L == R) continue;
            int delta = 0;
            delta -= Q(L) + Q(R);
            if(a[L] > a[R]) delta ++;
            add(L, -1);
            add(R, -1);
            swap(a[L], a[R]);
            add(L, 1);
            add(R, 1);
            delta += Q(L) + Q(R);
            if(a[L] > a[R]) delta --;
            cnt += delta;
            ans = min(ans, cnt);
        }
        printf("%lld\n", ans);
        for(int i = 1; i <= tot; ++ i) {
            T[i].cnt = T[i].lc = T[i].rc = 0;
        }
    }
    return 0;
}
View Code

 

Problem G. Matrix 01:18 (+) Solved by Dancepted&hat (数论,思维题)

思路:

  翻转次数为奇数的情况下,才对最终的答案有贡献。

  (x,y)的翻转次数 = x约数的个数 × y的约数的个数。

  设$x = \prod p_{i}^{t_{i}}(p_{i}是质数)$,则约数个数$d(x) = \prod(t_{i}+1)$,所有的$t_{i}$都必须是偶数,才对最终的答案有贡献。

  也就是说x和y是完全平方数。$[1, n]$区间内的完全平方数的平方根的范围为$[1, \left \lfloor \sqrt{n} \right \rfloor]$。

解法:

  答案就是$\left \lfloor \sqrt{n} \right \rfloor * \left \lfloor \sqrt{m} \right \rfloor$。

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

ll sq(ll x)
{
    ll t = sqrt(x) - 1;
    if(t < 0) t = 0;
    while((t + 1) * (t + 1) <= x) t++;
    return t;
}

int main() {
    fast;
    int t;
    cin >> t;
    while (t--) {
        ll n, m;
        cin >> n >> m;
        cout << sq(n) * sq(m) << endl;
    }
    return 0;
}
View Code

 

Problem H. Curious 03:43 (+1) Solved by Dancepted&hat (数论 容斥 莫比乌斯函数)

需要使劲分析复杂度的一道数论题。

WA了一发是因为加记忆化的时候,忘记用long long了。

题目大意:

  给出一个长度为n的数列a,满足$a_{i}<=m$,k次询问,每次给出一个x,回答:

  $\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(a_{i}, a_{j})==x]$

思路:

  用$sum_{i}$表示数列中为i的倍数的数的数量。

  ①对于x = 1的情况:

  $\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(a_{i}, a_{j})==1]$

  所有1的倍数的gcd至少为1,这些数的数量就是$sum_{1}*sum_{1}$,但这里面要减去gcd为2、3、5...的那些数,但这样6、10、15之类的又被多减了。

  实际上$sum_{i}*sum_{i}$的系数正好是莫比乌斯函数$\mu(i)$。

  因此对于x = 1,有$ans = \sum_{m}^{i=1}\mu (i)sum_{i}^2$。

  预处理sum_{i}时,可以先O(n)地统计[1, m]内每个数的出现次数,然后用埃氏筛在O(mloglogm)的时间内计算sum。

  总的时间复杂度是O(n+mloglogm)的。

  ②同样的,对于一个给定的x,有:

  $\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(a_{i}, a_{j})==x] = \sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(\frac{a_{i}}{x}, \frac{a_{j}}{x})==1]$。

  把数列中所有x的倍数的数单独拿出来,并除x。再跑一遍①中的算法,就可以得到答案了,令m' = m / x,时间复杂度为$O(n + m'loglogm)。

  这样看起来总复杂度似乎高达$O(n*n + n*mloglogm))$。

  复杂度右边的部分实际上是$\sum^{m}_{x=1}\frac{m}{x}loglog(m/x) <=mlogm\sum^{m}_{x=1}\frac{1}{x}$,而$\frac{1}{x}$的上界是一个常数(百度了一下好像是$\frac{\pi^2}{6}$)

  并且,数列中的每个数,只会被①调用$d(a_{i})$次($d(n)$表示n的约数的个数),而d(n)的一个显然的上界是$2\sqrt{n}$,复杂度的左边实际上是$O(n\sqrt{n})$。

  对于重复的x,也可以记忆化。

代码:$O(n\sqrt{n}+mloglogm))$

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int a[N];
vector<int> V[N];
int prime[N], miu[N];
int cnt[N], sum[N];

ll solve(vector<int> &v) {
    int n = SZ(v);
    int m = 0;
    for(int i = 0; i < n; i++) {
        m = max(m, v[i]);
    }
    memset(cnt, 0, sizeof(int) * (m + 1));
    for(int i = 0; i < n; i++)
        cnt[v[i]]++;
    memset(sum, 0, sizeof(int) * (m + 1));
    // ll ans = (ll) n * n;
    ll ans = 0;
    for(int i = 1; i <= m; i++)
    {
        for(int j = i; j <= m; j += i) {
            sum[i] += cnt[j];
        }
        ans += (ll) miu[i] * sum[i] * sum[i];
    }
    return ans;
}

void solveTestCase() {
    int n, m, k;
    cin >> n >> m >> k;
    for(int i = 1; i <= m; i++) {
        V[i].clear();
        cnt[i] = 0;
    }
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        cnt[a[i]]++;
    }
    for(int i = 1; i <= m; i++) {
        for(int j = i; j <= m; j+=i) {
            for(int _ = 0; _ < cnt[j]; _++) {
                V[i].push_back(j / i);
            }
        }
    }
    map<int, ll> mp;
    for(int i = 0; i < k; i++) {
        int x;
        cin >> x;
        if(x > m || x <= 0) cout << 0 << endl;
        else {
            if(mp.count(x)) {
                cout << mp[x] << endl;
            }
            else {
                ll t = solve(V[x]);
                mp[x] = t;
                cout << t << endl;
            }
        }
    }
}
void getPrime_and_Miu() {
    miu[1] = 1;
    for (int i = 2; i <= N; i++) {
        if (!prime[i]) prime[++prime[0]] = i, miu[i] = -1;
        for (int j = 1; j <= prime[0] && prime[j] <= N/i; j++) {
            prime[i*prime[j]] = 1;
            miu[i*prime[j]] = miu[i] * (i%prime[j] ? -1 : 0);
            if (i%prime[j] == 0) break;
        }
    }
}
int main() {
    fast;
    getPrime_and_Miu();
    int t; cin >> t;
    while (t--) {
        solveTestCase();
    }
    return 0;
}
View Code

 

Problem I. World Tree 04:05 (+) Solved by Dancepted (经典贪心 + 树形dp)

思路:

  题目中有说到,如果当前节点存在孩子,就不能返回到父节点,换句话说就是在树上dfs。

  所以如果有策略可以确定dfs的顺序,那么这题就解决了。

 

  令$A_{u} = \sum_{v\in以u为根的子树}a_{v}$,$B_{u} = \sum_{v\in以u为根的子树}b_{v}$

  以节点$u$为根的子树,对答案的贡献可以分成两类:

  1、子树的根$u$与子树内所有其他节点之间产生的贡献,这部分是无法改变的,为$a_{u}*(B_{u} - b_{u})$;

  2、以$u$为根的子树,与以$u$的兄弟节点$u'$及其子树产生的贡献,有两种可能的情况:

    ①$A_{u}B_{u'}$,此时是先遍历u子树,再遍历u'子树;

    ②$A_{u'}B_{u}$,此时是先遍历u'子树,再遍历u子树;

  如果①>②,说明先遍历u子树更优,反之亦然。

  在dfs到下一层之前,根据这个规则对子节点排序后再遍历就可以了。

  这个贪心策略之前在CF上做过类似的题

代码:O(nlogn)

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

int n;
int a[N], b[N];
ll sa[N], sb[N];
vector<int> es[N];
ll ans = 0;

void dfsInit(int u, int fa) {
    sa[u] = a[u];
    sb[u] = b[u];
    for (int v : es[u]) if (v != fa) {
        dfsInit(v, u);
        sa[u] += sa[v];
        sb[u] += sb[v];
    }
}
bool cmp(int l, int r) {
    return sa[l]*sb[r] > sa[r]*sb[l];
}
void dfsSolve(int u, int fa) {
    ans += (ll)a[u]*(sb[u] - b[u]);

    sort(es[u].begin(), es[u].end(), cmp);
    ll tmp = sb[u] - b[u];
    for (int v : es[u]) if (v != fa) {
        dfsSolve(v, u);
        tmp -= sb[v];
        ans += sa[v] * tmp;
    }
}
void solveTestCase() {
    cin >> n;
    for (int i = 1; i <= n; i++) es[i].clear();
    for (int i = 1; i <= n; i++) cin >> b[i];
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n-1; i++) {
        int u, v; cin >> u >> v;
        es[u].push_back(v);
        es[v].push_back(u);
    }
    dfsInit(1, -1);
    ans = 0;
    dfsSolve(1, -1);
    cout << ans << endl;
}

int main() {
    fast;
    int t; cin >> t;
    while (t--) {
        solveTestCase();
    }
    return 0;
}
View Code

 

Problem J. Situation 01:55 (+1) Solved by hat(min-max)

少开了一个状态,WA了一发。

思路:

  总状态数是$3^{9}*2\approx 4*10^{4}$。可以dfs加剪枝,dfs中用min-max决策。

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e3 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

struct state{
    int a[3][3];
    int toint()
    {
        int p3 = 1;
        int res = 0;
        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                res += a[i][j] * p3;
                p3 *= 3;
            }
        }
        return res;
    }
    bool isend()
    {
        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                if(!a[i][j]) return 0;
            }
        }
        return 1;
    }
    int cal()
    {
        int t = 0;
        for(int i = 0; i < 3; i++)
        {
            int f = a[i][0];
            for(int j = 1; j < 3; j++)
            {
                if(a[i][j] != f) f = 0;
            }
            if(f) {
                if(f == 1) t++;
                else t--;
            }
        }
        for(int i = 0; i < 3; i++)
        {
            int f = a[0][i];
            for(int j = 1; j < 3; j++)
            {
                if(a[j][i] != f) f = 0;
            }
            if(f) {
                if(f == 1) t++;
                else t--;
            }
        }
        int f = a[0][0];
        for(int j = 1; j < 3; j++)
        {
            if(a[j][j] != f) f = 0;
        }
        if(f) {
            if(f == 1) t++;
            else t--;
        }
        f = a[0][2];
        for(int j = 1; j < 3; j++)
        {
            if(a[j][2-j] != f) f = 0;
        }
        if(f) {
            if(f == 1) t++;
            else t--;
        }
        return t;
    }
};

int res[30000][2];

int dfs(state s, int ismax)
{
    int x = s.toint();
    if(res[x][ismax] != -1000) {
        return res[x][ismax];
    }
    if(s.isend()) {
        return res[x][ismax] = s.cal();
    }
    if(ismax) 
    { // O:1
        for(int i = 0; i < 3; i++)
            for(int j = 0; j < 3; j++)
                if(s.a[i][j] == 0)
                {
                    state t = s;
                    t.a[i][j] = 1;
                    res[x][ismax] = max(dfs(t, !ismax), res[x][ismax]);
                }
    }
    else
    { // X:2
        res[x][ismax] = 1000;
        for(int i = 0; i < 3; i++)
            for(int j = 0; j < 3; j++)
                if(s.a[i][j] == 0)
                {
                    state t = s;
                    t.a[i][j] = 2;
                    res[x][ismax] = min(dfs(t, !ismax), res[x][ismax]);
                }
    }
    return res[x][ismax];
}

int main() {
    for(int i = 0; i < 30000; i++)
        res[i][0] = res[i][1] = -1000;
    int t;
    cin >> t;
    while (t--) {
        int ismax;
        cin >> ismax;
        state t;
        for(int i = 0; i < 3; i++)
        {
            string s;
            cin >> s;
            for(int j = 0; j < 3; j++){
                if(s[j] == '.') t.a[i][j] = 0;
                else if(s[j] == 'O') t.a[i][j] = 1;
                else t.a[i][j] = 2;
            }
        }
        cout << dfs(t, ismax) << endl;
    }
    return 0;
}
View Code

 

Problem L. Swimmer 00:30 (+1) Solved by Guapi (签到)

RE了一发是因为数组开小了。。。

代码:

#include <bits/stdc++.h>
#define eps 1e-6
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define lowbit(x) (x & (-x))
#define SZ(v) ((int)(v).size())
#define All(v) (v).begin(), (v).end()
#define mp(x, y) make_pair(x, y)
#define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
int n, m, q, a[N];
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    for(int i = 1; i <= n; ++ i) {
        scanf("%d", &a[i]);
    }
    int p, k;
    for(int i = 1; i <= q; ++ i) {
        scanf("%d%d", &p, &k);
        int y = (1LL * p * a[k] ) % (2 * m);
        if(y <= m) printf("%d\n", y);
        else printf("%d\n", 2 * m - y);
    }

    return 0;
}
View Code

 

Problem M. Upanishad 04:38 (+) Solved by Guapi (树状数组)

知道是热身赛的题,就没急着做。

思路:

  区间出现次数为偶数的异或和 = 区间出现过的数的异或和 xor 区间出现次数为奇数的异或和。

  出现次数为奇数的异或和比较简单,用异或和的前缀和算一算就可以了。(出现次数为偶数的异或和为0)

  区间出现过的数,可以这么求(好像是树状数组的一个trick?):遍历的时候如果一个数之前出现过,那么就把树状数组中,在之前出现的位置减去这个数,再在新的位置加上这个数。

代码:O(nlogn)

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N = 5e5 + 10;
const int M = 2500000;
int a[N], sum[N], n, q, ans[N];
int c[N];
map<int, int> mp;
struct node {
    int l, r, pos;
    bool operator<(const node &o)const {
        return r < o.r;
    }
};
node Q[N];
void add(int x, int val) {
    for(; x <= n;  x += lowbit(x)) {
        c[x] ^= val ;
    }
}
int ask(int x) {
    int res = 0;
    for(; x > 0; x -= lowbit(x)) {
        res ^= c[x];
    }
    return res;
}
int main()
{
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; ++ i) {
        scanf("%d", &a[i]);
        sum[i] = a[i] ^ sum[i - 1];
    }
    for(int i = 1; i <= q; ++ i) {
        scanf("%d%d", &Q[i].l, &Q[i].r);
        Q[i].pos = i;
    }
    sort(Q + 1, Q + q + 1);
    int L = 1;
    for(int i = 1; i <= q; ++ i) {
        while(L <= Q[i].r) {
            if(mp.count(a[L])){
                add(mp[a[L]], a[L]);
            }
            mp[a[L]] = L;
            add(L, a[L]);
            ++ L;
        }
        int res = sum[Q[i].r] ^ sum[Q[i].l - 1];
        int res1 = ask(Q[i].r) ^ ask(Q[i].l - 1);
        res ^= res1;
        ans[Q[i].pos] = res;
    }
    for(int i = 1; i <= q; ++ i) {
        printf("%d\n", ans[i]);
    }
    return 0;
}
View Code

 


 

  

总结:

  省赛还是偏轻松愉快的2333,不过刚回来还是会犯一些低级错误,还有个trick因为太久没做题了,WA了一发才想起来。

  C(+1)的解法实现起来的时候,如果正向转移的话,同一层会自己影响自己,这是背包问题里比较常见的trick了,模拟赛中写的时候没有意识到这个问题。

  F(+5)暴露出的问题是读题不够仔细+对板子不过熟悉。首先是没有读到完整的题意,实际上做题的时候,如果有特别地说明区间跨度不超过100之类的,肯定是会针对这个条件来设计解法的。其次是题意读了个大概,然后发现和树套树的板子很像,就抛开题目直接上板子了。虽然实际上树套树确实可做,但因为数据量比较特殊,有更简单的解法,可以省下不少机时。

  G(+)虽然是一发过的,但是思路上绕了个大弯。我们队对各种数论筛法还是不够熟悉,上来有点念念不忘数论筛法,误以为这是个筛子题,想着怎么用筛子做。过了很久才发现这就是个结论题。

  H(+1)代码写的没问题,交之前顺手加了个记忆化,结果记忆化的部分没有用long long。

  J(+1)如果是我写的话,我可能也会犯这样的错误吧。动手写中期题的时候,还是要把思路想清楚再摸键盘。边写边想还是很容易漏掉一些点的。

  L(+1)数组开太小了。。

  还有就是我们讨论的时候容易想到什么就直接说,但我觉得这样讨论的效率其实是比较低的。我觉得应该先独立思考,有什么关键的进展,或者想到了什么解法,再和队友讨论。

 

posted on 2021-01-22 13:17  Danceped  阅读(660)  评论(0编辑  收藏  举报

导航