8月14日模拟赛
前言
\(请一定不要相信题目给的数据范围,怎么着也得多开一个1e4\)
T4 空间开小了,\(RE\).
100 ——> 60
T1 还原排列
题目描述
小 \(K\) 喜欢排列,比如喜欢计算循环排列的逆序对个数。
对于一个长度为 \(n\) 的数组 \(P=p_1,p_2……p_n\),我们认为它是一个长度为 \(n\) 排列,当且仅当它包含了 \([1,n]\) 中的所有正整数恰好一次。
这天,小 \(K\) 得知了一个加密排列的方法。具体地,对于一个排列 \(P\),加密后为一个长度相同的数组 \(S\)。其中,第 \(i\) 位的加密值 \(s_i\) 为满足 \(j<i\) \(&&\) \(p_j<p_i\) 的所有 \(p_j\) 的和。
小 \(K\) 认为这个加密方式太水了,于是随手写了一个程序解密,但是发现 \(TA\) 的程序跑了一天还没跑出来。按照套路,你能帮助小 \(K\) 通过数组 \(S\) 还原出排列 \(P\) 吗?
数据规模
对于 \(100%\) 的数据, $1 ≤ n ≤ 10_6 $
解: 树状数组倍增
这个题甚至还有写并查集的大佬(虽然我不懂
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+105;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int n,ans[N],s[N],d[N],mp;
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int v){while(x<=n){d[x]+=v;x+=lowbit(x);}}
inline int query(int x){
int pos=0,sum=0;
for(int i=mp;i>=0;i--)
if((1<<i)+pos<=n && sum+d[pos+(1<<i)]<=x) {pos+=(1<<i);sum+=d[pos];}
return pos+1;
}
signed main(){
n=read();mp=log2(n);
for(int i=1;i<=n;i++) {s[i]=read();add(i,i);}
while((1<<mp)<n)mp++;if((1<<mp)>=n)mp--;
for(int i=n;i>=0;i--){ans[i]=query(s[i]);add(ans[i],-ans[i]);}
for(int i=1;i<n;i++)printf("%d ",ans[i]);
printf("%d",ans[n]);
return 0;
}
T2 借书
题目描述:
\(Dilhao\) 一共有 \(n\) 本教科书,每本教科书都有一个难度值,他每次出题的时候都会从其中挑两本教科书作为借鉴,如果这两本书的难度相差越大, \(Dilhao\) 出的题就会越复杂,也就是说,一道题的复杂程度等于两本书难度差的绝对值。
这次轮到 \(ldxxx\) 出题啦,他想要管 \(Dilhao\) 借 \(m\) 本书作为参考去出题, \(Dilhao\) 想知道,如果 \(ldxxx\) 在 \(Dilhao\) 给出的 \(m\) 本书里挑选难度相差最小的两本书出题,那么 \(ldxxx\) 出的题复杂程度最大是多少?
解:
先排序后二分答案。
\(code\):
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=2e5+105;
int n,m,a[N],c[N],l=1e9+7,r;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
bool C(int x){
int sum=0,cnt=0;
for(int i=1;i<n;i++){
sum+=c[i];
if(sum>=x){cnt++;sum=0;}
if(cnt>=m-1)return 1;
}
return 0;
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){a[i]=read();r=max(r,a[i]);}
sort(a+1,a+n+1);for(int i=1;i<n;i++){c[i]=a[i+1]-a[i];l=min(l,c[i]);}
//for(int i=1;i<n;i++)printf("%d ",c[i]);
while(l<=r){
if(r-l==1){if(C(r))l=r;break;}
if(C(mid))l=mid;else r=mid;
}
printf("%d",l);
return 0;
}
T3 追捕
题目描述:
\(Duan2baka\):“ \(jmsyzsfq\) 天下第一蠢!”
\(jmsyzsfq\):“你说什么?!”
于是 \(Duan2baka\) 开始了逃亡的旅程,而 \(jmsyzsfq\) 也开始追捕 \(Duan2baka\) 。 他们来到了一个有 \(n\) 个节点的有向图,迅速逃跑的 \(Duan2baka\) 会首先降落在有向图的某个节点 \(T\) 上,因为怕发出声音,他会永远静止不动。
而随后跟来的 \(jmsyzsfq\) 会降落在图上随机一个节点 \(S\) 上(可以与 \(T\) 相同),并可以沿着图中的有向边随意走动。因为 \(jmsyzsfq\) 有着敏锐的嗅觉而且绝顶聪明,他一定会沿着正确的方向寻找 \(Duan2baka\) 。
你可以认为,如果图中存在一条合法的从 \(S\) 到 \(T\) 的路径,那么 \(jmsyzsfq\) 一定会抓到 \(Duan2baka\) ——因此 \(jmsyzsfq\) 能否追捕到 \(Duan2baka\) 在他刚刚降落在图上的时候就已经确定了。
现在请你帮帮 \(jmsyzsfq\) ,在他降落前告诉他有多大的概率能够追捕到 \(Duan2baka\) ,并告诉他想要追到 \(Duan2baka\) 他可以降落在哪些节点上。
输入格式
输入第一行包含三个整数 \(n\) , \(m\) , \(opt\) ,表示图中有 \(n\) 个节点, \(m\) 条有向边; \(opt\) 为 \(0\) 或 \(1\),含义见输出格式所述。
输入第二行至 \(m+1\) 行每行两个整数 \(u\) , \(v\) 描述了图中一条从 \(u\) 到 \(v\) 的有向边。
输入第 \(m+2\) 行一个整数 \(T\) 表示 \(Duan2baka\) 降落的位置。
输出格式
如果输入的 \(opt\) 为 \(0\) ,只需要输出 \(jmsyzsfq\) 能够追捕到Duan2bakaDuan2baka的概率。
如果输入的 \(opt\) 为 \(1\) ,输出两行,第一行输出 \(jmsyzsfq\) 能够追捕到 \(Duan2baka\) 的概率;第二行按从小到大输出想要追到 \(Duan2baka\) 他可以降落的节点编号,编号间用空格隔开。
对于 \(jmsyzsfq\) 能够追捕到 \(Duan2baka\) 的概率,是一个既约分数,形如“$a / b \(”(\)a,b$为整数),使 \(gcd(a,b)=1\) ,如果 \(a=b\) ,输出\(1/1\) 。
\(注意,这个题的输出格式那个opt,有些人因此挂了 50 分\)
解:
先反向建图,把问题从多少点能到达目标点转化成目标点能到达哪些点。跑一边\(bfs\)即可。(如果是\(bfs\)就不用怕环了
听说缩点+dfs也可以做?(感觉代码不好写就没去研究
\(code\):
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int n,m,opt,nex[N],h[N],to[N],cnt,d[N],tot=1,x,y,T;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
inline void fadd(int x,int y){to[++cnt]=x;nex[cnt]=h[y];h[y]=cnt;}
void bfs(int s){
memset(d,0,sizeof(d));
queue<int> q;q.push(s);d[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=h[x];i;i=nex[i]){
if(d[to[i]])continue;
tot++;d[to[i]]=1;q.push(to[i]);
}
}
}
int main(){
n=read();m=read();opt=read();
for(int i=1;i<=m;i++){x=read();y=read();fadd(x,y);}
T=read();bfs(T);
int GCD=gcd(n,tot);
printf("%d/%d\n",tot/GCD,n/GCD);
if(opt==1){for(int i=1;i<=n;i++)if(d[i])printf("%d ",i);}
return 0;
}
T4 空间活动
题目描述
贝茜和佩奇正在玩一款游戏,在游戏开始会生成一个有 \(n\) 个点 \(m\) 条单向边的地图,经过每条边需要花费价格为 \(H_i\) 的费用( \(H_i≤1000\) )。
但是如果两个点可以互相到达,那么这两个点之间需要花费的价格就变为 \(0\) 。
总共有 \(k\) 关,每一关会给你两个点 \(a\) , \(b\) ( \(0<a,b≤n\) ),让你求从 \(a\) 走到 \(b\) 的最小花费。
大概意思就是:有一个有向图,环里的边的权值全变为0,求最短路
解
跑一个缩点,重新建一个有向无环图,然后直接搞最短路。
\(code\):
#include<bits/stdc++.h>
using namespace std;
const int INF=2147483647;
const int N=1e4+105;
//变量
struct qwq{int nex,to,val;}e[N<<1];
struct pwp{int fr,t,v;}w[N<<1];
struct ovo{
int id,va;
friend bool operator < (ovo a,ovo b){return a.va>b.va;}
}s[N];
priority_queue<ovo> Q;
int n,m,k,x,y,z;//主函数
int num,low[N],dfn[N],q[N],h[N],tot,top,c[N];
bool vis[N<<1];//缩点
int cnt;//建图
bool vi[N<<1];//最短路
//快读
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
//缩点,num,low,dfn,q,vis,h,nex,to,tot,top,c
void tarjan(int x){
low[x]=dfn[x]=++num;q[++top]=x;vis[x]=1;
for(int i=h[x];i;i=e[i].nex){
if(!dfn[e[i].to]){tarjan(e[i].to);low[x]=min(low[x],low[e[i].to]);}
else if(vis[e[i].to]){low[x]=min(low[x],dfn[e[i].to]);}
}
if(low[x]==dfn[x]){
tot++;
while(q[top+1]!=x){c[q[top]]=tot;vis[q[top--]]=0;}
}
}
//val,cnt
inline void add(int x,int y,int z){
e[++cnt].to=y;e[cnt].val=z;
e[cnt].nex=h[x];h[x]=cnt;
}
//最短路,vi
void dij(int x){
for(int i=0;i<=n;i++)s[i].id=i,s[i].va=INF;memset(vi,0,sizeof(vi));
s[x].va=0;Q.push(s[x]);
while(!Q.empty()){
x=Q.top().id;Q.pop();
if(vi[x])continue;vi[x]=1;
for(int i=h[x];i;i=e[i].nex){
if(vi[e[i].to])continue;
if(s[e[i].to].va>s[x].va+e[i].val){
s[e[i].to].va=s[x].va+e[i].val;
Q.push(s[e[i].to]);
}
}
}
}
int main(){//n,m,k,x,y,z
n=read();m=read();k=read();
for(int i=1;i<=m;i++){
w[i].fr=read();w[i].t=read();w[i].v=read();
add(w[i].fr,w[i].t,w[i].v);
}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
memset(e,0,sizeof(e));memset(h,0,sizeof(h));cnt=0;
for(int i=1;i<=m;i++){
x=w[i].fr;y=w[i].t;
if(c[x]!=c[y])add(c[x],c[y],w[i].v);
}
while(k--){
x=read();y=read();
if(c[x]==c[y]){printf("0\n");continue;}
dij(c[x]);
if(s[c[y]].va==INF)printf("No\n");
else printf("%d\n",s[c[y]].va);
}
return 0;
}
$ The END $