• 2019南京网络赛


    A The beautiful values of the palace

    知识点:模拟,树状数组+离散+二维前缀和

    1. 根据坐标算值(先把中心当作1,然后用n*n减去,反过来算更容易)
    2. 将特殊点加入队列
    3. 读入询问,对于询问((x_1,y_1,x_2,y_2)) ,如果(mp[x][y]) 为二维前缀和,那么答案就是(mp[x_2][y_2]-mp[x_2][y_1-1]-mp[x_1-1][y_2]+mp[x_1-1][y_1-1])
    4. 将所有询问需要求得的二维前缀的坐标也加入队列,按照x为第一参考进行排序,对y离散化
    5. 排序之后从小到大依次考虑,即x是从小到大考虑的,每次遇到新的特殊点就插入到树状数组中,遇到查询((x,y)) 就在树状数组中查询(1,y-1)的和即可。
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1000010;
    int T;
    int n,m,p;
    ll d[N],c[N];
    struct node{
        int x1,y1,x2,y2;
    };
    vector<node> qu;//查询
    struct Node{
        int x,y;
        int type;//type为0表示添加新点
    }a[N];
    bool cmp(Node a,Node b){
        if(a.x == b.x){
            if(a.y == b.y)return a.type == 0;
            return a.y < b.y;
        }
        return a.x < b.x;
    }
    vector<int> v;//离散y
    void add(int x,int y){
        for(;x<N;x+=x&-x)c[x] += y;
    }
    ll ask(int x){
        ll res = 0;
        for(;x;x-=x&-x)res += c[x];
        return res;
    }
    map<int,map<int,int>> mp;
    int id[N];
    ll get(int x,int y){
        int cx = n / 2 + 1;
        int cy = n / 2 + 1;//(cx,cy)即中心坐标
        int k = max(abs(x-cx),abs(y-cy));//k表示在第几圈
        if(k == 0)return 1ll * n * n;
        //printf("
    (%d,%d) k : %d
    ",x,y,k);
        ll res = d[k-1];//圈内有多少个
        //分4个case计算
        if(y-cy == k && x < cx + k){//在上层
            res += cx + k - x;
        }else if(cx - x == k && y < cy + k){//在左侧
            res += k * 2 + cy + k - y;
        }else if(cy - y == k && x > cx - k){//在下层
            res += k * 4 + x - (cx - k);
        }else if(x - cx == k){//在右侧
            res += k * 6 + y - (cy - k);
        }
        res = 1ll * n * n - res;//最后倒过来,因为上面是按照中心为1算的
        return res + 1;
    }
    int calc(ll x){
        int res= 0;
        while(x){res += x % 10;x /= 10;}
        return res;
    }
    int main(){
        scanf("%d",&T);
        d[0] = 1;d[1] = 8;
        for(int i=2;i<N;i++)d[i] = d[i-1] + 8;
        for(int i=1;i<N;i++)d[i] += d[i-1];//计算i圈以内有多少个点
        while(T--){
            mp.clear();v.clear();qu.clear();
            memset(c,0,sizeof c);
            scanf("%d%d%d",&n,&m,&p);
            for(int i=1;i<=m;i++){
                int x,y;
                scanf("%d%d",&x,&y);
                a[i] = {x,y,0};
            }
            for(int i=1;i<=p;i++){
                int x1,x2,y1,y2;
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
                qu.push_back({x1,y1,x2,y2});
                a[++m] = {x1-1,y1-1,1};
                a[++m] = {x2,y2,1};
                a[++m] = {x1-1,y2,1};
                a[++m] = {x2,y1-1,1};
            }
            for(int i=1;i<=m;i++){
                v.push_back(a[i].y);
            }
            sort(v.begin(),v.end());
            v.erase(unique(v.begin(),v.end()),v.end());
            sort(a+1,a+m+1,cmp);
            for(int i=1;i<=m;i++){
                id[i] = lower_bound(v.begin(),v.end(),a[i].y) - v.begin() + 1;
            }
            for(int i=1;i<=m;i++){
                if(a[i].type == 0)add(id[i],calc(get(a[i].x,a[i].y)));
                else if(a[i].type == 1)mp[a[i].x][a[i].y] = ask(id[i]);
            }
            for(int i=0;i<p;i++){
                int x1 = qu[i].x1,x2 = qu[i].x2,y1 = qu[i].y1,y2 = qu[i].y2;
                ll res = mp[x2][y2] + mp[x1-1][y1-1] - mp[x1-1][y2] - mp[x2][y1-1];
                printf("%lld
    ",res);
            }
        }
        return 0;
    }
    

    D Robots

    (d[u]) 为 u 到 n 的期望天数,那么有:

    (d[u] = d[u] * {1over outdeg[u]} + {1over outdeg[u]} * sum d[v] + 1)

    (f[u]) 为 u 到 n 的期望消耗,那么有:

    (f[u] = {1over outdeg[u]} *(f[u] + d[u]) + {1over outdeg[u]} * sum (f[v]+d[v]) + 1)

    因为你在 u 这个点过去的这一天,会对之后的每一天的贡献都额外增加 1,所以要加 d[u] 和 (sum d[v])

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e6+10;
    int head[N],ver[N],nxt[N],tot,deg[N],sz[N];
    int T,n,m;
    void add(int x,int y){
        ver[++tot] = y;nxt[tot] = head[x];head[x] = tot;
    }
    double f[N],d[N];
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&m);
            tot = 0;
            for(int i=1;i<=n;i++){
                head[i] = 0;f[i] = d[i] = 0;deg[i] = 0;sz[i] = 0;
            }
            for(int i=1;i<=m;i++){
                int x,y;scanf("%d%d",&x,&y);
                add(y,x);
                deg[x]++;sz[x]++;
            }
            queue<int> q;
            q.push(n);
            while(q.size()){
                int x = q.front();q.pop();
                for(int i=head[x];i;i=nxt[i]){
                    int y = ver[i];
                    f[y] += f[x];
                    d[y] += d[x];
                    if(-- deg[y] == 0){
                        //if刚进来时,f[y] 和 d[y] 为 累加和
                        f[y] = ((d[y]+sz[y]+1)/sz[y]+f[y]+d[y]+sz[y]+1)/sz[y];
                        d[y] = (d[y] + sz[y] + 1)/sz[y];
                        q.push(y);
                    }
                }
            }
            printf("%.2f
    ",f[1]);
        }
        return 0;
    }
    

    F Greedy Sequence

    权值线段树维护出现过的数的最大值,即[l,r]表示当前所有数中,值在([l,r])范围的数的最大值为多少

    滑动区间维护线段树,每次查询[1,x-1] 中出现的最大数,如果没有答案为0

    最后记忆化搜索即可

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int T;
    const int N = 100010;
    int a[N],n,k,b[N],d[N];
    struct Seg{
        int l,r;
        int mx;
    }t[4*N];
    void build(int p,int l,int r){
        t[p].l = l;t[p].r = r;
        if(l == r){
            t[p].mx = 0;return;
        }
        int mid = l + r >> 1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        t[p].mx = 0;
    }
    void upd(int p,int x,int add){
        if(t[p].l == t[p].r && t[p].l == x){
            t[p].mx = add == 1 ? x : 0;
            return;
        }
        int mid = t[p].l + t[p].r >> 1;
        if(mid >= x)upd(p*2,x,add);
        if(mid < x)upd(p*2+1,x,add);
        t[p].mx = max(t[p*2].mx,t[p*2+1].mx);
    }
    int query(int p,int l,int r){
        if(t[p].l >= l && t[p].r <= r){
            return t[p].mx;
        }
        int mid = t[p].l + t[p].r >> 1;
        int res = 0;
        if(mid >= l)res = max(res,query(p*2,l,r));
        if(mid < r)res = max(res,query(p*2+1,l,r));
        return res;
    }
    ll get(int x){
        if(b[x] == 0)return d[x] = 1;
        if(d[x])return d[x];
        d[x] = get(b[x]) + 1;
        return d[x];
    }
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d%d",&n,&k);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            for(int i=1;i<=n;i++)d[i] = 0;
            int l = 1,r=min(1+k,n);
            build(1,1,n);
            for(int i=l;i<=r;i++){
                upd(1,a[i],1);
            }
            for(int i=1;i<=n;i++){
                b[a[i]] = query(1,1,a[i]-1);
                if(i-l>=k){
                    upd(1,a[l],0);
                    l++;
                }
                if(r<n){
                    upd(1,a[++r],1);
                }
            }
            ll res = 0;
            for(int i=1;i<=n;i++)res += get(a[i]);
            for(int i=1;i<=n;i++){
                if(i>1)printf(" ");
                printf("%d",get(i));
            }
            puts("");
        }
        return 0;
    }
    

    H. Holy Grail

    每次跑最短路即可,返回答案为正则添加负权边,否则添加正边(注意不能用迪杰斯特拉)

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1010;
    typedef long long ll;
    const ll INF = 1e18;
    int head[N],ver[N],Nxt[N],tot;
    int v[N],n,m;
    ll edge[N],d[N];
    void add(int x,int y,ll z){
        ver[++tot] = y;edge[tot] = z;Nxt[tot] = head[x];head[x] = tot;
    }
    ll dij(int s,int t){
        queue<int> q;
        for(int i = 0; i <= n + 5; i++){
            d[i] = INF;v[i] = 0;
        }
        d[s] = 0; q.push(s); v[s] = 1;
        while(q.size())
        {
            int x = q.front(); q.pop();
            v[x] = 0;
            for(int i = head[x]; i; i = Nxt[i]){
                int y = ver[i];
                if(d[y] > d[x] + edge[i])
                {
                    d[y] = d[x] + edge[i];
                    if(v[y] == 0){
                        v[y] = 1;
                        q.push(y);
                    }
                }
            }
        }
        return d[t];
    }
    int main(){
        int T;scanf("%d",&T);
        for(int o=0;o<T;o++){
            scanf("%d%d",&n,&m);
            tot = 0;
            for(int i=1;i<=n;i++)head[i] = 0;
            for(int i=1;i<=m;i++){
                int x,y;
                ll z; 
                scanf("%d%d%lld",&x,&y,&z);
                x++;y++;
                add(x,y,z);
            }
            for(int j=1;j<=6;j++){
                int s,t;
                scanf("%d%d",&s,&t);
                s++;t++;
                ll dis = dij(t,s);
                printf("%lld
    ",-dis);
                add(s,t,-dis);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    eclipse 工程没有build path
    Redis中RedisTemplate和Redisson管道的使用
    Spring多开发环境配置
    Commons Configuration之三Properties文件
    Commons Configuration之二基本特性和AbstractConfiguration
    Commons Configuration之一简介
    Commons DbUtils
    这可能是最容易入门的socket教程了
    图解leetcode —— 124. 二叉树中的最大路径和
    图解leetcode —— 395. 至少有K个重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/1625--H/p/11443960.html
Copyright © 2020-2023  润新知