The Preliminary Contest for ICPC Asia Nanjing 2019【A B D E F】

题目地址:The Preliminary Contest for ICPC Asia Nanjing 2019 

F. Greedy Sequence

题意:

题意有点绕,自己读吧

分析:

首先题目要求字典序最大,假设Sk在A数组的位置是pos,那么Sk的下一个数Sk+1一定是A[pos-k......pos+k]中小于Sk的最大值,类似dp的思想,以Sk开头的序列中不为0的数dp[Sk] = dp[Sk+1] + 1;怎么找Sk+1呢?扫一遍A[1....N]数组,同时用set维护A[i-pos,i+pos]的值,二分set即可找到

代码:

#include <bits/stdc++.h>

#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
const int maxn = 2e5+25;
int T,n,k,a[maxn],ans[maxn],pre[maxn];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&k);
        for(int i = 1;i <= n; ++i) scanf("%d",a+i);
        set<int> s; int L = 1,R = min(k+1,n);
        for(int i = 1;i <= R; ++i) s.insert(a[i]);
        for(int i = 1;i <= n; ++i){
            while(R-i<k&&R+1<=n) s.insert(a[++R]);
            while(i-L>k) s.erase(a[L++]);
            set<int>::iterator it = s.lower_bound(a[i]);
            if(it == s.begin()) pre[a[i]] = 0;
            else pre[a[i]] = *(--it);
        }
        ans[1] = 1,ans[0] = 0;
        for(int i = 2;i <= n; ++i) ans[i] = ans[pre[i]]+1;
        for(int i = 1;i < n; ++i) cout << ans[i] << " " ;
        cout << ans[n] << '\n';
    }
    return 0;
}

B. super_log

题意:

自己读吧

分析:

所给的函数是一个递归的形式,每递归一次就是+1,容易推出最后X = ((a^a)^a)^a......【b个a的幂】,算是一个原题【BZOJ-3884】(简单版),【CF-906D】(加强版),实现就是欧拉广义降幂的递归形式,注意b=0时输出(1 mod m)

代码:

#include <bits/stdc++.h>

#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int maxn = 2e5+25;
LL a,b,m;
LL Eular(LL n){
    LL phi = n;
    for(int i = 2;1ll*i*i <= n;++i){
        if(n%i==0){
            phi -= phi/i;
            while(n%i==0) n = n/i;
        }
    }
    if(n>1) phi -= phi/n;
    return phi;
}
LL Quickpow(LL a,LL x,LL mod){   //魔改一下快速幂,因为要判断幂和phi的大小关系
    LL ans = 1; bool flag = false;
    while(x){
        if(x&1){
            ans = ans * a;
            if(ans >= mod){
                ans %= mod;
                flag = true;
            }
        }
        x >>= 1; if(!x) break;
        a = a * a;
        if(a >= mod){
           a %= mod;
           flag = true;
        }
    }
    return flag ? ans%mod+mod : ans%mod;
}
LL solve(int l,int r,LL mod){
    if(l==r||mod==1) return a<mod? a%mod:a%mod+mod;
    LL phi = Eular(mod);
    return Quickpow(a,solve(l+1,r,phi),mod);
}
int main(){
    int T; cin >> T;
    while(T--){
        cin >> a >> b >> m;
        if(b == 0) printf("%d\n",(int)(1%m));
        else cout << solve(1,b,m)%m << '\n';
    }
    return 0;
}

D. Robots

题意:

自己读吧

分析:

期望一般都是倒着推,定义dp[x][0] 表示 x 到 N的期望天数,dp[x][1]代表x 到 N 的期望消耗;首先很好理解:dp[x][0] = (dp[V1][0]+dp[V2][0]+...+dp[Vk][0])/(k+1) + dp[x][0] / (k+1) + 1,然后又有:dp[x][1] = (dp[V1][1]+dp[V2][1]+...+dp[Vk][1])/(k+1) + dp[x][1] / (k+1) + dp[x][0];为什么是加dp[x][0]呢?自己强行yy了一下:如果 x 到 N的期望天数是 m,那么也就是说x 到 N期望经过 m个节点到达N,那么在x节点做了决策,消耗势必会+1,也就是会给m个节点都增加1消耗;化简公式,一遍记忆化搜索就好了

代码:

#include <bits/stdc++.h>

#define sz(x) (int)(x).size()
using namespace std;
typedef long long LL;
const int maxn = 1e5+16;
int n,m,u,v,T;
vector<int> g[maxn];
double dp[maxn][2];
bool vis[maxn];
void dfs1(int x){
    vis[x] = true;
    double sum1 = 0.0,sum2 = 0.0;
    for(int i = 0;i < sz(g[x]); ++i){
        int v = g[x][i];
        if(!vis[v]) dfs1(v);
        sum1 += dp[v][0];
        sum2 += dp[v][1];
    }
    if(sz(g[x])) dp[x][0] = (sum1+sz(g[x])+1.0)/sz(g[x]);
    else dp[x][0] = 0.0;
    if(sz(g[x])) dp[x][1] = (sum2+(sz(g[x])+1.0)*dp[x][0])/sz(g[x]);
    else dp[x][1] = 0.0;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i = 0;i <= n; ++i) g[i].clear();
        while(m--){
            scanf("%d %d",&u,&v);
            g[u].push_back(v);
        }
        memset(vis,false,sizeof(vis)); dfs1(1); 
        printf("%.2f\n",dp[1][1]);
    }
    return 0;
}

A. The beautiful values of the palace

题意:

还是自己读吧

分析:

只要知道每个点的值,那么询问就是求一个矩形内的元素和,这个可以用扫描线+树状数组快速求,具体做法:先离线将矩形拆分成上下两条线段,将所有的线段和点按高度排序,从低到高依次扫描每一位置,如果扫到的是点,那么就将a[x] += val,如果扫到的是一条线段,那么这条线段下面的点肯定都已经扫过了,那么这条线段下面所有点的和就是a[L.....R]的和,这个可以用树状数组维护,那么一个矩形内的和就是上边界的所求到的和 减 下边界所求到的和;还有一个问题就是要快速求得一个格子里的数是多少,可以先考虑它属于第几圈,再考虑它是这一圈的第几个就行了

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1e6+16;
int T,n,m,p,x,y,xx,yy,cnt; 
int cal(LL x){
    int res = 0;
    while(x){
        res += x%10;
        x /= 10;
    }
    return res;
}
LL sum[maxn],res[maxn],tr[maxn];
void init(int n){
    LL m = n;
    for(int i = 1;i <= n/2+1; ++i,m-=2)
        sum[i] = sum[i-1]+4*m-4;
}
LL Find(int x,int y,int n){
    int k,kk;
    if(x >= (n+1)/2) k = n-x+1;
    else k = x;
    if(y >= (n+1)/2) kk = n-y+1;
    else kk = y;
    k = min(k,kk); LL res = sum[k-1];          //前k-1圈有sum[k-1]个数;最大的圈为第一圈
    int sx = n-k+1,sy = n-k+1,r = n-2*(k-1);   //第k圈的起始坐标(sx,sy)
    if(x == sx) return res+sy-y+1;             //暴力这个点在那条边上
    else if(y == sy-r+1) return res+r+sx-x;
    else if(x == sx-r+1) return res+2*r-1+y-(sy-r+1);
    else return res+3*r-2+x-(sx-r+1);
}
struct edge{
    int f,L,R,h,id;
    LL val;
}e[maxn<<2];
bool cmp(edge a,edge b){
    if(a.h == b.h){               //下边界和上边界有点区别
        if((a.f==1&&b.f==0)||(b.f==1&&a.f==0)) return a.f>b.f;
        else return a.f < b.f;
    }
    return a.h < b.h;
}
inline int lowbit(int x){
    return (-x) & x;
}
void updata(int pos,LL val){
    while(pos < maxn){
        tr[pos] += val;
        pos += lowbit(pos);
    }
}
LL query(int pos){
    LL ans = 0;
    while(pos > 0){
        ans += tr[pos];
        pos -= lowbit(pos);
    }
    return ans;
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d %d",&n,&m,&p); init(n);
        cnt = 0; memset(tr,0,sizeof(tr));
        while(m--){
            scanf("%d %d",&x,&y);
            e[cnt++] = (edge){0,x,y,y,0,cal(Find(x,y,n))};
        }
        for(int i = 1;i <= p; ++i){
            scanf("%d%d%d%d",&x,&y,&xx,&yy);
            e[cnt++] = (edge){1,x,xx,y,i,0};
            e[cnt++] = (edge){2,x,xx,yy,i,0};
        }
        sort(e,e+cnt,cmp);
        for(int i = 0;i < cnt; ++i){
            if(e[i].f == 0) updata(e[i].L,e[i].val);
            else if(e[i].f == 1) sum[e[i].id] = query(e[i].R)-query(e[i].L-1); 
            else res[e[i].id] = query(e[i].R)-query(e[i].L-1)-sum[e[i].id];
        }
        for(int i = 1;i <= p; ++i) cout << res[i] << '\n' ;
    }
    return 0;
    
}

E. K Sum

题解直通车~~

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值