The Preliminary Contest for ICPC Asia Nanjing 2019

A The beautiful values of the palace

题目链接
题意:在一个n*n的螺旋矩阵,给你m个坐标(x,y)代表此下标是存在数的。再给q个询问,询问左下角(x1,y1)和右上角(x2,y2)为矩阵中存在的数的数位和。
思路:对于一个坐标(x,y)在螺旋矩阵中的数值,可以通过近似O(1)的计算得出。计算矩阵中有多少个存在的值,我们通过对于m个坐标进行排序,建立主席树。x坐标存在重复所以需要lower_bound(),upper_bound()二分。

#include <bits/stdc++.h>

#define maxn 100005
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;

struct node{
    int l,r;
    ll cnt;
    node(){
        l=r=cnt=0;
    }
}T[maxn*22];

struct point{
    int x,y;
}p[maxn];

bool cmp(point a,point b){
    if(a.x==b.x)
        return a.y<b.y;
    return a.x<b.x;
}

int get_beauty(ll n, ll x, ll y){
    ll top = n - y, bottom = y - 1, lft = x - 1, rht = n - x;
    ll dis = min(min(top,bottom),min(lft,rht));
    ll has = n * n - ((n - dis * 2) * (n - dis * 2));
    ll edge = n - 2 * dis - 1;
    if(x == n - dis){
        has += top - dis + 1;
    }
    else if(y == dis + 1){
        has += edge + rht - dis + 1;
    }
    else if(x == dis + 1){
        has += 2 * edge + bottom - dis + 1;
    }
    else{
        has += 3 * edge + lft - dis + 1;
    }
    int wei = 0;
    while(has){
        wei += has % 10;
        has /= 10;
    }
    return wei;
}

int rt[maxn];
int xCopy[maxn];
int tot=0;
int n,m,q;

void update(int &x,int y,int l,int r,int pos,int val){
    T[++tot]=T[y];
    T[tot].cnt+=val;
    x=tot;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(T[x].l,T[y].l,l,mid,pos,val);
    else
        update(T[x].r,T[y].r,mid+1,r,pos,val);
}

int ans=0;
void query(int x,int y,int l,int r,int L,int R){
    if (L <= l && r <= R) {
        ans += T[y].cnt - T[x].cnt;
        return;
    }
    int mid = l + r >> 1;
    if (L <= mid)query(T[x].l, T[y].l, l, mid, L, R);
    if (R > mid)query(T[x].r, T[y].r, mid + 1, r, L, R);
}

void init(){
    tot=0;
}

void input(){
    scanf("%d%d%d",&n,&m,&q);
    int i;
    for(i=1;i<=m;i++)
        scanf("%d%d",&p[i].x,&p[i].y);

    sort(p+1,p+1+m,cmp);

    for(i=1;i<=m;i++){
        update(rt[i],rt[i-1],1,n,p[i].y,get_beauty(1ll*n,1ll*p[i].x,1ll*p[i].y));
        xCopy[i]=p[i].x;
    }


    int x1,y1,x2,y2;    
    for(i=1;i<=q;i++){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int pos1=lower_bound(xCopy+1,xCopy+1+m,x1)-xCopy;
        int pos2=upper_bound(xCopy+1,xCopy+1+m,x2)-xCopy-1;
//        cout<<pos1<<' '<<pos2<<endl;

        ans=0;
        query(rt[pos1-1],rt[pos2],1,n,y1,y2);
        printf("%d\n",ans);
    }



}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        init();
        input();
//        solve();
    }
}

D Robots

题目链接
题意:问一个机器人从图中1到n所需要的燃油值期望为多少?每次耗费的燃油值,是当前已经历的天数值。
思路:拓扑求出天数的期望值,再拓扑求出耗油的期望值。图片说明

图片说明

其中t[u]是天数期望,f[u]是耗油期望

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

int t;
int n,m;
vector <int>G[maxn];
vector <int>g[maxn];
int indeg[maxn];
int indegCopy[maxn];
double day[maxn];
double ans[maxn];

void topo(){
    int i;
    queue <int>q;
    for(i=1;i<=n;i++)
        if(!indeg[i]){
            day[i]=0;
            q.push(i);
        }


    while(!q.empty()){
        int f=q.front();
        q.pop();
        for(int i=0;i<G[f].size();i++){
            indeg[G[f][i]]--;
            if(!indeg[G[f][i]])
            {
                q.push(G[f][i]);
                for(int j=0;j<g[G[f][i]].size();j++)
                    day[G[f][i]]+=day[g[G[f][i]][j]];
                day[G[f][i]]+=(g[G[f][i]].size()+1);
                day[G[f][i]]/=g[G[f][i]].size();
            }        
        }
    } 
}

void topo2(){
    int i;
    queue <int>q;
    for(i=1;i<=n;i++)
        if(!indeg[i]){
            ans[i]=0;
            q.push(i);
        }

    while(!q.empty()){
        int f=q.front();
        q.pop();
        for(int i=0;i<G[f].size();i++){
            indeg[G[f][i]]--;
            if(!indeg[G[f][i]])
            {
                q.push(G[f][i]);
                for(int j=0;j<g[G[f][i]].size();j++)
                    ans[G[f][i]]+=ans[g[G[f][i]][j]];
                ans[G[f][i]]+=(g[G[f][i]].size()+1)*day[G[f][i]];
                ans[G[f][i]]/=g[G[f][i]].size();
            }        
        }
    } 
}

void input(){
    scanf("%d%d",&n,&m);
    int i,u,v;
    for(i=1;i<=n;i++){
        G[i].clear();
        g[i].clear();
    }

    memset(day,0,sizeof day);
    memset(ans,0,sizeof ans);
    memset(indeg,0,sizeof indeg);
    memset(indegCopy,0,sizeof indegCopy);

    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        G[v].push_back(u);
        g[u].push_back(v);
        indeg[u]++;
        indegCopy[u]++;
    }
}

void solve(){
    topo();

//    for(int i=1;i<=n;i++)
//        cout<<day[i]<<endl;

    for(int i=1;i<=n;i++)
        indeg[i]=indegCopy[i];

    topo2();

    printf("%.2lf\n",ans[1]);
}

int main(){
    scanf("%d",&t);
    while(t--){
        input();
        solve();
    }
}

F Greedy Sequence

题目链接
题意:有n个序列S1、S2....Sn ,每个序列定义:1、Si[1]=i。2、每个序列Si都是非递增序列 3、序列Si中的上一个值和下一个值在给定排列a中的距离差需要小于等于k。4、满足Si字典序大于Si-1 5、未确定的值用0代替。
要求寻找每个序列中非零值个数。
思路:滑动窗口+multiset二分获得元素的下一个元素,记忆化O(n)记录答案

#include <bits/stdc++.h>

#define maxn 100005
#define inf 0x3f3f3f3f
using namespace std;

int n,k;
int a[maxn];
int dis[maxn];
int ans[maxn];
multiset <int>st;
multiset <int>::iterator it;

void init(){
    st.clear();

    int i;
    for(i=1;i<=k;i++)
        st.insert(a[i]);

    for(i=1;i<=n;i++){
        st.erase(a[i]);
        if(i>1)
            st.insert(a[i-1]);
        if(i>k+1)
            st.erase(a[i-k-1]);

        if(i+k<=n)
            st.insert(a[i+k]);

        it=st.lower_bound(a[i]);
        if(it!=st.begin())
        {
            it--;
            dis[a[i]]=*it;
        }else
            dis[a[i]]=0;
    }
}

void input(){
    scanf("%d%d",&n,&k);
    int i;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);

    init();

//    for(i=1;i<=n;i++){
//        cout<<a[i]<<" "<<dis[a[i]]<<endl;
//    }

    for(i=1;i<=n;i++){
        if(dis[i]!=0)
            ans[i]=ans[dis[i]]+1;
        else
            ans[i]=1;
    }

    for(i=1;i<=n;i++)
        printf("%d%c",ans[i],i==n?'\n':' ');
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
//        solve();
    }
}

H Holy Grail

题目链接
题意:n个点m条边,存在负边但不存在负环的图。给出6个查询,询问点u和v,求v到u增加边(可以为负),使权值最小。
思路:输出v到u的最短路的相反数,并将u到v,权值为刚才求出的答案这条边加入到图中

#include <bits/stdc++.h>

#define maxn 305
#define inf 0x3f3f3f3f
using namespace std;

int n,m;

struct edge{
  int to,cost;
};

typedef pair<int,int>P;

vector <edge> G[maxn];
int dis[maxn];

void dij(int a)
{
  priority_queue<P,vector<P>,greater<P> >q;

  memset(dis,0x3f,sizeof(dis));
  dis[a]=0;
  q.push(P(0,a));
  while(!q.empty())
  {
    P temp=q.top();
    q.pop();
    int v=temp.second;
    if(dis[v]<temp.first)continue;
    for(int i=0;i<G[v].size();i++)
    {
      edge e=G[v][i];
      if(dis[e.to]>dis[v]+e.cost)
      {
        dis[e.to]=dis[v]+e.cost;
        q.push(P(dis[e.to],e.to));
      }
    }
  }
}

void input(){
    scanf("%d%d",&n,&m);
    int i;
    for(i=0;i<n;i++)
        G[i].clear();
    int u,v,w;
    for(i=0;i<m;i++){
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back((edge){v,w});
    }

    int s,t;
    int q=6;
    while(q--){
        scanf("%d%d",&s,&t);
        dij(t);
        printf("%d\n",-dis[s]);
        G[s].push_back((edge){t,-dis[s]});
    }
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        input();
//        solve();
    }
}