题目地址: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