• The Preliminary Contest for ICPC Asia Nanjing 2019


    传送门

    A. The beautiful values of the palace

    题意:
    给出一个(n*n)的矩阵,并满足(n)为奇数,矩阵中的数从右上角开始往下,类似于蛇形填数那样来填充。
    之后会给出(m)个有效数字的坐标,坐标左下角为((1,1)),右上角为((n,n)),其余数字都为(0)
    之后会有多个询问,每个询问给出一个子矩阵,问子矩阵中元素和为多少。

    思路:

    • 矩阵每个位置的数值可以(O(1))计算;
    • 将每个询问拆分成四个询问,接下来处理的问题就是怎么维护一个二维前缀和。
    • 对于一个点((x,y)),很显然我们要找的点就是((x_i,y_i))并满足(x_ileq x, y_ileq y)
    • 这样类似的二维偏序,有很多做法,比如cdq分治;
    • 但其实并不需要,类似于扫描线的思想,按(y)从小到大扫描,并不断加点,利用树状数组维护即可。

    代码写得有点凌乱,很多冗余...

    Code
    #include <bits/stdc++.h>
    #define y1 skljalkd
    #define MP make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5, M = 1e5 + 5;
    int T;
    struct node{
        int x, y, v, id;
        bool operator < (const node &A) const {
            if(A.y == y && A.x == x) return id < A.id;
            if(A.y == y) return x < A.x;
            return y < A.y;
        }
    }a[M], b[N], d[N];
    int n, m, p;
    int X[N], Y[N];
    inline int getv(int x,int y) {
    	ll k=min(min(x,y),min(n-x+1,n-y+1));
    	ll ans=(ll)n*n-(ll)(n-2*(k-1))*(n-2*(k-1));
    	if(x==n-k+1) {
    		ans+=(n-k+1 -y+1);
    	} else if(y==n-k+1) {
    		ans+=n-2*k+2 + n-2*k+1 +n-2*k +(x-k+1);
    	} else if(x==y) {
    		ans+=n-2*k+2 + n-2*k+1;
    	} else if(x>y) {
    		ans+=n-2*k+2 + (n-k-x+1);
    	} else if(x<y) {
    		ans+=n-2*k+2 + n-2*k + y-k+1;
    	}
    	int res = 0;
    	while(ans) {
            res += ans % 10;
            ans /= 10;
    	}
    	return res;
    }
    void Hash() {
        X[0] = Y[0] = 0;
        for(int i = 1; i <= m; i++) X[++X[0]] = a[i].x, Y[++Y[0]] = a[i].y;
        for(int i = 1; i <= 4 * p; i++) X[++X[0]] = b[i].x, Y[++Y[0]] = b[i].y;
        sort(X + 1, X + X[0] + 1);
        sort(Y + 1, Y + Y[0] + 1);
        X[0] = unique(X + 1, X + X[0] + 1) - X - 1;
        Y[0] = unique(Y + 1, Y + Y[0] + 1) - Y - 1;
        for(int i = 1; i <= m; i++) a[i].x = lower_bound(X + 1, X + X[0] + 1, a[i].x) - X;
        for(int i = 1; i <= m; i++) a[i].y = lower_bound(Y + 1, Y + Y[0] + 1, a[i].y) - Y;
        for(int i = 1; i <= 4 * p; i++) b[i].x = lower_bound(X + 1, X + X[0] + 1, b[i].x) - X;
        for(int i = 1; i <= 4 * p; i++) b[i].y = lower_bound(Y + 1, Y + Y[0] + 1, b[i].y) - Y;
    }
    vector <pair<int, int> > col[N];
    int c[N];
    ll ans[N];
    int lowbit(int x) {return x & (-x);}
    void upd(int x, int v) {
        for(; x < N; x += lowbit(x)) c[x] += v;
    }
    ll query(int x) {
        ll res = 0;
        for(; x; x -= lowbit(x)) res += c[x];
        return res;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            memset(c, 0, sizeof(c));
            cin >> n >> m >> p;
            for(int i = 1; i <= m; i++) {
                cin >> a[i].x >> a[i].y;
                a[i].v = getv(a[i].x, a[i].y);
                a[i].id = 0;
            }
            for(int i = 1; i <= p; i++) {
                int x1, y1, x2, y2;
                cin >> x1 >> y1 >> x2 >> y2;
                --x1, --y1;
                int x3 = x1, y3 = y2;
                int x4 = x2, y4 = y1;
                int v1 = getv(x1, y1), v2 = getv(x2, y2), v3 = getv(x3, y3), v4 = getv(x4, y4);
                b[4 * i - 1] = {x1, y1, v1, 4 * i};
                b[4 * i] = {x2, y2, v2, 4 * i - 2};
                b[4 * i - 2] = {x3, y3, v3, 4 * i - 1};
                b[4 * i - 3] = {x4, y4, v4, 4 * i - 3};
            }
            Hash();
            for(int i = 1; i <= Y[0]; i++) col[i].clear();
            for(int i = 1; i <= m; i++) d[i] = a[i];
            for(int i = 1; i <= 4 * p; i++) d[i + m] = b[i];
            int tot = m + 4 * p;
            sort(d + 1, d + tot + 1);
            for(int i = 1; i <= tot; i++) {
                col[d[i].y].push_back(MP(i, d[i].id == 0));
            }
            for(int i = 1; i <= Y[0]; i++) {
                for(auto it : col[i]) {
                    if(it.se == 1) {
                        upd(d[it.fi].x, d[it.fi].v);
                    }
                    if(it.se == 0) {
                        int now = it.fi;
                        ans[d[now].id] = query(d[now].x);
                    }
                }
            }
            for(int i = 1; i <= p; i++) {
                ll res = ans[4 * i - 2] + ans[4 * i] - ans[4 * i - 1] - ans[4 * i - 3];
                cout << res << '
    ';
            }
        }
        return 0;
    }
    

    B. super_log

    题意:
    定义一下式子:

    [log_a^*(x)=left{ egin{aligned} &-1,&x<1\ &1+log_a^*(log_ax),&xgeq 1 end{aligned} ight. ]

    求最小的(x),满足(log_a^*(x)geq b)

    思路:
    稍微推一下式子,会变成这样:

    [log_a^*(x)=b+log_a^*(log_a{log_a{cdots log_ax}}) ]

    那么就有(x=a^{a^{cdots ^{a}}}),这里共(b)(a)
    之后利用广义欧拉降幂搞一下即可。
    注意取模函数,这是一个技巧,有了它我们就可以不用管广义欧拉降幂中的条件了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1000005;
    bool vis[N];
    int prime[N], phi[N], tot;
    void pre() {
        phi[1] = 1;
        for(int i = 2; i < N; i++) {
            if(!vis[i]) {
                vis[i] = 1; prime[++tot] = i;
                phi[i] = i - 1;
            }
            for(int j = 1; j <= tot && prime[j] * i < N; j++) {
                vis[i * prime[j]] = 1;
                if(i % prime[j] == 0) {
                    phi[i * prime[j]] = phi[i] * prime[j];
                    break;
                }
                phi[i * prime[j]] = phi[i] * (prime[j] - 1);
            }
        }
    }
    ll Mod(ll a, ll b) {
        return a < b ? a : a % b + b;
    }
    int qp(ll a, ll b, ll p) {
        int ans = 1;
        while(b) {
            if(b & 1) ans = Mod(ans * a, p);
            a = Mod(a * a, p);
            b >>= 1;
        }
        return ans;
    }
    int calc(ll a, ll b, ll m) {
        if(m == 1 || !b) return 1;
        int p = phi[m];
        int x = calc(a, b - 1, p);
        return qp(a, x, m);
    }
    int T;
    int a, b, m;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        pre();
        cin >> T;
        while(T--) {
            cin >> a >> b >> m;
            ll ans = calc(a, b, m) % m;
            cout << ans << '
    ';
        }
        return 0;
    }
    
    

    D. Robots

    题意:
    给出一张有向无环图。
    现在有一个机器人在上面走,每一天可以行动一步,这一步可以选择停留原地或者沿着某一条边走。
    定义每一天的消耗为已经经过天数的消耗。
    求最终到达(n)点时的期望消耗值。

    思路:
    这个题怎么这么难读,感觉读不懂QAQ。
    假设(dp2[u])为从(u)出发,到达(n)的期望消耗值。那么就有:

    [dp2[u]=frac{dp2[u]+dp[u]+1}{out[u]+1}+sum_{(u,v)in Edge}frac{dp2[v]+dp[v]+1}{out[u]+1} ]

    分别表示停留在原地的期望消耗以及走向下一个点的期望消耗,加起来就是目前点的期望消耗。
    那这里的(dp[u])指的是什么?
    指的就是从(u)(n)的期望天数,有了它就相当于求出当前这天的期望消耗。
    (dp[u])类似推一遍就行。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n, m;
    vector <int > g[N];
    double dp[N][2];
    bool vis[N];
    double dfs1(int u) {
        if(u >= n) return dp[n][0] = 0;
        if(vis[u]) return dp[u][0];
        vis[u] = 1;
        double res = 0;
        for(auto it : g[u]) res += dfs1(it);
        int x = g[u].size();
        res /= (double)(x + 1);
        res = 1.0 * (x + 1) / x * (res + 1);
        return dp[u][0] = res;
    }
    double dfs2(int u) {
        if(u >= n) return dp[n][1] = 0;
        if(vis[u]) return dp[u][1];
        vis[u] = 1;
        double res = 0;
        for(auto it : g[u]) res += dfs2(it) + dp[it][0];
        res += dp[u][0];
        int x = g[u].size();
        res /= (double)(x + 1);
        res = 1.0 * (x + 1) / x * (res + 1);
        return dp[u][1] = res;
    }
    int main() {
    //    ios::sync_with_stdio(false); cin.tie(0);
        int T; cin >> T;
        while(T--) {
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= n; i++) g[i].clear();
            for(int i = 1; i <= m; i++) {
                int u, v; cin >> u >> v;
                g[u].push_back(v);
            }
            fill(vis + 1, vis + n + 1, false);
            dfs1(1);
            fill(vis + 1, vis + n + 1, false);
            dfs2(1);
            printf("%.2f
    ", dp[1][1]);
        }
        return 0;
    }
    
    

    E. K Sum

    题意:
    定义函数:

    [f_n(k)=sum_{l_1=1}^{n}sum_{l_2=1}^{n}cdots sum_{l_k=1}^{n}(gcd(l_1,l_2,cdots,l_k))^2 ]

    其中(nleq 10^9,2leq kleq 10^{10^5})
    要求:

    [sum_{i=2}^kf_n(i) \% 1e9+7 ]

    思路:
    这个题给我的感觉就是数论大杂烩...融进来了好多东西,也正好对之前的学习相当于一次复习。
    开始愉快(套路)的推式子环节:

    [egin{aligned} f_n(k)&=sum_{l_1=1}^{n}sum_{l_2=1}^{n}cdots sum_{l_k=1}^{n}(gcd(l_1,l_2,cdots,l_k))^2\ &=sum_dd^2sum_{l_1=1}^{n}sum_{l_2=1}^{n}cdots sum_{l_k=1}^{n}[gcd(l_1,l_2,cdots,l_k)=d]\ &=sum_dd^2sum_{l_1=1}^{frac{n}{d}}sum_{l_2=1}^{frac{n}{d}}cdots sum_{l_k=1}^{frac{n}{d}}[gcd(l_1,l_2,cdots,l_k)=1]\ &=sum_dd^2sum_{l_1=1}^{frac{n}{d}}sum_{l_2=1}^{frac{n}{d}}cdots sum_{l_k=1}^{frac{n}{d}}sum_{t|gcd(l_1,l_2,cdots,l_k)}mu(t)\ &=sum_dd^2sum_tmu(t)sum_{l_1=1}^{frac{n}{td}}sum_{l_2=1}^{frac{n}{td}}cdots sum_{l_k=1}^{frac{n}{td}}1 end{aligned} ]

    之后令(T=td),就有:

    [=sum_{T=1}^{n}sum_{d|T} d^2mu(frac{T}{d})sum_{l_1=1}^{frac{n}{T}}sum_{l_2=1}^{frac{n}{T}}cdots sum_{l_k=1}^{frac{n}{T}}1 ]

    一直化到这一步都是比较套路的部分。
    观察后面那一堆求和式,其实可以等价于(lfloorfrac{n}{T} floor^k),而(sum_{d|T} d^2mu(frac{T}{d}))就相当于(mu * id^2)
    所以所求式子可以化为:

    [egin{aligned} sum_{i=2}^kf_n(i)&=sum_{i=1}^ksum_{T=1}^{n}sum_{d|T} d^2mu(frac{T}{d})lfloorfrac{n}{T} floor^k\ &=sum_{T=1}^n(sum_{i=1}^klfloorfrac{n}{T} floor^k)(sum_{d|T} d^2mu(frac{T}{d})) end{aligned} ]

    因为注意到(k)的范围可能很大,所以就交换一下和式,发现第一个括号内就相当于一个等比数列求和,那么就有一个公式,并且在公比不为1的情况下,(k)可以直接模(1e9+6)来处理。如果公比为1,特判一下即可。

    主要就是后面那一堆和式怎么搞,设(g(n)=sum_{d|n}d^2mu(frac{n}{d}))
    可以发现,(g)也为一个积性函数,那么我们就可以将值线性筛筛出来:(g(1)=1,g(p)=p^2),如果我们能找到(g(p^k))的值,那么就很好筛了。
    因为后面有一个(mu),所以(g(p^k)=-p^{2k-2}+p^{2k}=p^{2k-2}(p^2-1))
    那么就有(g(p^k)=g(p^{k-1})cdot p^2)
    由于是积性函数,那么就很好筛了~每一个素因子直接独立出来计算,最后乘起来,写在代码里就直接拆就是了。

    观察到第一个括号部分可以直接数论分块,那么这就需要我们求后面一部分的前缀和,所以直接杜教筛来搞后面那一块就行,找(I)卷积一下即可。
    复杂度:(O)(肯定能过)。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5, MOD = 1e9 + 7, inv6 = 166666668;
    ll n, k, k2;
    char s[N];
    int T, tot;
    ll sum[N], prime[N], g[N];
    bool vis[N];
    void pre() {
        g[1] = vis[1] = 1;
        for(int i = 2; i < N; i++) {
            if(!vis[i]) {
                vis[i] = 1; prime[++tot] = i;
                g[i] = (1ll * i * i - 1) % MOD;
            }
            for(int j = 1; j <= tot && i * prime[j] < N; j++) {
                vis[i * prime[j]] = 1;
                if(i % prime[j] == 0) {
                    g[i * prime[j]] = g[i] * prime[j] % MOD * prime[j] % MOD;
                    break;
                }
                g[i * prime[j]] = g[i] * g[prime[j]] % MOD;
            }
        }
        for(int i = 1; i < N; i++) sum[i] = (sum[i - 1] + g[i]) % MOD;
    }
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    ll calc(int q) {
        if(q == 1) return k2 - 1;
        ll b = 1 - q + MOD;
        b = qp(b, MOD - 2);
        ll a = 1ll * q * (1 - qp(q, k) + MOD) % MOD;
        return (a * b % MOD - q + MOD) % MOD;
    }
    unordered_map <int, int> mp;
    ll djs(int x) {
        if(x < N) return sum[x];
        if(mp.find(x) != mp.end()) return mp[x];
        ll ans = 1ll * x * (x + 1) % MOD * (2 * x + 1) % MOD * inv6 % MOD;
        for(ll i = 2, j; i <= x; i = j + 1) {
            j = x / (x / i);
            ans -= (j - i + 1) * djs(x / i) % MOD;
            ans %= MOD;
        }
        if(ans < 0) ans += MOD;
        return mp[x] = ans;
    }
    int main() {
        //freopen("input.in", "r", stdin);
        ios::sync_with_stdio(false); cin.tie(0);
        pre();
        cin >> T;
        while(T--) {
            cin >> n >> s + 1;
            int len = strlen(s + 1);
            k = k2 = 0;
            for(int i = 1; i <= len; i++) {
                k = (k * 10 + (s[i] - '0')) % (MOD - 1);
                k2 = (k2 * 10 + (s[i] - '0')) % MOD;
            }
            ll ans, res = 0;
            for(ll i = 1, j; i <= n; i = j + 1) {
                j = n / (n / i);
                ans = calc(n / i) * (djs(j) - djs(i - 1) + MOD) % MOD;
                res = (res + ans) % MOD;
            }
            cout << res << '
    ';
        }
        return 0;
    }
    
    

    F. Greedy Sequence

    很容易分析,对一个数求答案的话,就是找相应区间内小于他的最大的数,所以就有很多种做法。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int T, n, k;
    int maxv[N << 2];
    void push_up(int o) {
        maxv[o] = max(maxv[o << 1], maxv[o << 1|1]);
    }
    void build(int o, int l, int r) {
        maxv[o] = 0;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(o << 1, l, mid); build(o << 1|1, mid + 1,r);
    }
    void update(int o, int l, int r, int p, int v) {
        if(l == r) {
            maxv[o] = v; return;
        }
        int mid = (l + r) >> 1;
        if(p <= mid) update(o << 1, l, mid, p, v);
        else update(o << 1|1, mid + 1, r, p, v);
        push_up(o);
    }
    int query(int o, int l, int r, int L, int R) {
        if(L > R) return 0;
        if(L <= l && r <= R) return maxv[o];
        int mid = (l + r) >> 1;
        int res = 0;
        if(L <= mid) res = max(res, query(o << 1, l, mid, L, R));
        if(R > mid) res = max(res, query(o << 1|1, mid + 1, r, L, R));
        return res;
    }
    int a[N], ans[N], pos[N];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> k;
            build(1, 1, n);
            for(int i = 1; i <= n; i++) cin >> a[i], pos[a[i]] = i;
            for(int i = 1; i <= n; i++) {
                int mx = query(1, 1, n, max(1, pos[i] - k), pos[i] - 1);
                mx = max(mx, query(1, 1, n, pos[i] + 1, min(n, pos[i] + k)));
                ans[i] = ans[mx] + 1;
                update(1, 1, n, pos[i], i);
            }
            for(int i = 1; i <= n; i++) cout << ans[i] << " 
    "[i == n];
        }
        return 0;
    }
    

    H. Holy Grail

    签到题,跑六次最短路即可。

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 3e2+5,MAXM = 5e2+5,MOD = 20130427,INF = 0x3f3f3f3f,N=100050;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-9;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    #define vii vector<pair<int,int>>
    #define vi vector<int>
    using namespace std;
    
    struct Edge{
        int u,v;
        ll w;
    }e[MAXM];
    int t,n,m,start[10],target[10];
    ll d[MAXN];
    int main(){
        ios::sync_with_stdio(false);cin.tie(0);
        //freopen("../A.in","r",stdin);
        //freopen("../A.out","w",stdout);
        cin>>t;
        while(t--){
            cin>>n>>m;
            for(int i=1;i<=m;i++){
                cin>>e[i].u>>e[i].v>>e[i].w;
            }
            for(int i=1;i<=6;i++)cin>>start[i]>>target[i];
            for(int s=1;s<=6;s++){
                for(int i=0;i<n;i++)d[i]=INF;
                d[target[s]]=0;
                for(int i=1;i<n;i++){
                    for(int j=1;j<=m;j++){
                        d[e[j].v]=min(d[e[j].v],d[e[j].u]+e[j].w);
                    }
                }
                cout << -d[start[s]]<<'
    ';
                e[++m]={start[s],target[s],-d[start[s]]};
            }
        }
        return 0;
    }
    

    I. Washing clothes

    题意:
    现在有(n)个人要洗衣服,有一个洗衣机。
    这些人都会在(a_i)的时间来,并且有两种选择,花(y)的时间手洗衣服,或者花(x)的时间洗衣机洗。洗衣机每次只能洗一个人的衣服,手洗可以大家一起洗。
    现在问当(xin [1,y])时,洗完所有衣服的最终时刻是多少。

    思路:

    • 很显然我们要从后面开始考虑,这样才能让最终时刻缩短。
    • 并且有个这样的观察:最优情况中存在一个位置(p)(p)之后的所有人都是洗衣机洗,前面的都是手洗。
    • 简略证明(口胡):若(i,k)位置的人都是洗衣机洗,(i<j<k),并且(j)是用手洗的,那么很显然,(i)用洗衣机洗肯定不如(j)用洗衣机洗。
    • 所以就直接枚举后面有多少个人用洗衣机来洗就行了。
    • 但复杂度是(O(n^2))的,这里又有个观察,就是用洗衣机的人不会超过(lfloorfrac{m}{x} floor)个。
    • 为什么?
    • 人再多点,对于某个人来说,还不如他手洗呢。

    所以复杂度优化到了(O(nlogn))

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 6;
    int a[N];
    int n, y;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        while(cin >> n >> y) {
            for(int i = 1; i <= n; i++) cin >> a[i];
            sort(a + 1, a + n + 1);
            for(int x = 1; x <= y; x++) {
                ll ans = 0;
                int p = min(n, y / x);
                if(p < n) ans = a[n - p] + y;
                for(int i = n; i > n - p; i--) ans = max(ans, a[i] + 1ll * (n - i + 1) * x);
                cout << ans << " 
    "[x == y];
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    《一线架构师》之Refined Architecture阶段
    可修改性战术分析
    上周学习总结
    软件质量属性之可修改性
    《信息领域热词分析》实现六种质量属性
    质量属性的六个常见属性应用场景(淘宝篇)
    软件架构师如何工作
    信息领域热词分析
    结合ssh谈MVC架构模式
    PythonCPPJava的异同
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11445496.html
Copyright © 2020-2023  润新知