T1-取餐码![](https://i-blog.csdnimg.cn/blog_migrate/1bdc57a98481cf4f64322b2dc2bcab86.png)
找到n以内的质数,并求出第m个质数,显然就是求质数的板子题
5e6的数据范围,暴力打法肯定会被卡,所以采用埃氏筛或欧拉筛。相比来说埃氏筛更好理解,但还是要对欧拉筛的模板牢牢掌握,防止数据更大时被卡。
代码如下(欧拉筛模板):
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qr()
{
ll x=0,f=0;char ch=0;
while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f? -x:x;
}
const ll maxn=1e7;
ll n,m;
ll prime[maxn];
ll tot;
ll v[maxn];
void init()
{
n=qr();m=qr();
}
void get_prime()
{
prime[1]=1;
for(int i=2;i<=n;i++)
{
if(v[i]==0)
{
v[i]=i;
prime[++tot]=i;
}
for(int j=1;j<=tot;j++)
{
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
}
void print()
{
cout<<tot<<' '<<prime[m];
}
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
init();
get_prime();
print();
}
T2-堆人塔![](https://i-blog.csdnimg.cn/blog_migrate/561c90dc860207150dab29e3559f0962.png)
对这个排列不断分段,很明显可以用dfs模拟分层的这个过程,三个参数分别为左端点、右端点以及当前的层数。但dfs的时间复杂度为O(),只能拿到前40分。
再次寻找dfs的缺陷,我们发现每次分段后都用了一层循环寻找区间最大值,于是优化思路就是快速找到区间最大值。
求区间最大值的方法可以用ST表进行构造,以O(log n )的复杂度先构造好ST表后,查找的时间复杂度就是O(1),这样整体的时间复杂度就为O(n*log n)
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qr()
{
ll x=0,f=0;char ch=0;
while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f? -x:x;
}
ll n;
struct fun
{
ll num,pla,flo;
}per[1000000];
ll pos[1000000];
ll st[1000000][30];
void init()
{
n=qr();
for(int i=1;i<=n;i++)
{
per[i].num=qr();
pos[per[i].num]=i;
}
}
void sol()
{
for(int i=1;i<=n;i++)
{
st[i][0]=per[i].num;
}
int LOG=log2(n);
for(int j=1;j<=LOG;j++)
{
for(int i=1;i<=n-(1<<j)+1;i++)
{
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
ll ask(ll x,ll y)
{
int s=log2(y-x+1);
return max(st[x][s],st[y-(1<<s)+1][s]);
}
void dfs(ll lef,ll rig,ll F)
{
if(lef>rig) return ;
if(lef==rig)
{
per[lef].flo=F;
return ;
}
int pp=ask(lef,rig);
per[pos[pp]].flo=F;
dfs(lef,pos[pp]-1,F+1);
dfs(pos[pp]+1,rig,F+1);
}
int main()
{
freopen("tower.in","r",stdin);
freopen("tower.out","w",stdout);
init();
sol();
dfs(1,n,0);
for(int i=1;i<=n;i++)
{
cout<<per[i].flo<<' ';
}
}
同寝大佬用单调栈优化到了O(n),这里也把代码放出来(无删改):
#include<bits/stdc++.h>//貌似要做单调栈? 每个数都是它左边或右边第一个大于它的儿子(取小的)
using namespace std;
const int N = 1e5 + 10;
int n, a[N], cnt[N], L[N], R[N], head[N], tot, st;
stack< int > s;
struct edge{
int v, last;
}E[N * 2];
void Get(){
for(int i = 1; i <= n; i++){
L[i] = R[i] = 1e6;
}
for(int i = 1; i <= n; i++){//找每个数的两边第一个大于它的数
while(!s.empty() && a[i] > a[s.top()]){
int t = s.top();
R[t] = i;//右边第一个比它大的
s.pop();
}
if(!s.empty()){
int t = s.top();
L[i] = t;//左边第一个比他大的
}
s.push(i);
}
}
void add(int u, int v){
E[++tot].v = v;
E[tot].last = head[u];
head[u] = tot;
}
void dfs(int s){
for(int i = head[s]; i != 0; i = E[i].last){
int v = E[i].v;
cnt[v] = cnt[s] + 1;
dfs(v);
}
}
int main(){
freopen("tower.in", "r", stdin);
freopen("tower.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
Get();
for(int i = 1; i <= n; i++){//建图
int l_max = L[i], r_max = R[i], f = 0; //L[i], R[i]分别表示左右两边比第i个数大的数
if(l_max == 1e6 && r_max == 1e6) st = i;
else{
if(l_max == 1e6) add(r_max, i);
else if(r_max == 1e6) add(l_max, i);
else{
if(a[l_max] > a[r_max]) add(r_max, i);
else add(l_max, i);
}
}
}
dfs(st);
for(int i = 1; i <= n; i++){
if(i == 1) printf("%d", cnt[i]);
else printf(" %d", cnt[i]);
}
printf("\n");
return 0;
}
T3-钦定IOI选手![](https://i-blog.csdnimg.cn/blog_migrate/4c0826e0c2fe816020b53c62020b2aaf.png)
看到中位数,显然与数的绝对大小无关,只与相对大小有关,于是我们可以把≥x的数改为1,<x的数都改为-1,得到一个新数列,此时便有结论:对于一个区间内的各数之和>0,其z则该序列的中位数一定≥x
因此考虑每次二分x,记录 b 数组的前缀和和前缀最小值,在区间长度 ≥k 时差分计算一下即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qr()
{
ll x=0,f=0;char ch=0;
while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f? -x:x;
}
const ll maxn=1e6;
ll n,k;
ll a[maxn];
ll t[maxn];
ll sum[maxn];
ll tot;
ll ans;
void init()
{
n=qr();k=qr();
for(int i=1;i<=n;i++)
a[i]=qr();
}
bool check(int x)
{
memset(t,0,sizeof(t));
memset(sum,0,sizeof(sum));
for(int i=1; i<=n; i++)
{
if(a[i]>=x) t[i]=1;
else t[i]=-1;
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+t[i];
}
tot=1e9;
for(int i=k;i<=n;i++)
{
tot=min(tot,sum[i-k]);
if(sum[i]>tot) return 1;
}
return 0;
}
void work()
{
ll l=1,r=1e9;
ll mid;
while(l+1<r)
{
ll mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
if(check(r)) ans=r;
else ans=l;
}
void print()
{
cout<<ans;
}
int main()
{
freopen("plunder.in","r",stdin);
freopen("plunder.out","w",stdout);
init();
work();
print();
}
T4-攻打恶魔之巅
将问题抽象为图论问题。
将每一级台阶看作是一个节点。它可以连向的结点有两类,一类是步长范围内能到达的点,边长为 1。另一类是传送的点,边长为 0,但是这个要消耗一次传送次数的。于是很容易就想到了分层图最短路。
设 di,j 为消耗了 j 次传送由 1 走到 i 点的最小步数。当通过第一类边转移的时候(记转移到的点为 v), 更新的是 dv,j, 第二类的转移更新的是 dv,j+1。
我们知道 边权要么是 1,要么是 0 ,所以用 01bfs 转移就可以得到 100 分了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int qr()
{
ll x=0,f=0;char ch=0;
while(!isdigit(ch)) {f|=ch=='-';ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return f? -x:x;
}
int n,m,k;
const int maxn=500100;
struct fun
{
int y,v,nxt;
}edge[25000100];
int link[maxn];
int len;
int a[maxn];
int dis[maxn][30];
bool vis[maxn][30];
int ans=2e9;
struct node
{
int pos,vav;
};
deque <node> que;
void insert(int xx,int yy,int val)
{
edge[++len].nxt=link[xx];
link[xx]=len;
edge[len].y=yy;
edge[len].v=val;
}
void init()
{
n=qr();m=qr();k=qr();
for(int i=1;i<=n;i++)
{
a[i]=qr();
}
for(int i=1;i<=n;i++)
{
for(int j=max(i-m,1);j<=min(i+m,n);j++)
{
insert(i,j,1);
}
}
}
void DIJ()
{
memset(dis,10,sizeof(dis));
que.push_back((node){1,0});
dis[1][0]=0;
while(!que.empty())
{
node py=que.front();
que.pop_front();
if(vis[py.pos][py.vav]) continue;
vis[py.pos][py.vav]=1;
if(a[py.pos]&&py.vav!=k)
{
if(!vis[a[py.pos]][py.vav+1]&&dis[a[py.pos]][py.vav+1]>dis[py.pos][py.vav])
{
dis[a[py.pos]][py.vav+1]=dis[py.pos][py.vav];
que.push_front((node){a[py.pos],py.vav+1});
}
}
for(int i=link[py.pos];i;i=edge[i].nxt)
{
if(!vis[edge[i].y][py.vav]&&dis[edge[i].y][py.vav]>dis[py.pos][py.vav]+edge[i].v)
{
dis[edge[i].y][py.vav]=dis[py.pos][py.vav]+edge[i].v;
que.push_back((node){edge[i].y,py.vav});
}
}
}
}
void work()
{
DIJ();
for(int i=0;i<=k;i++)
{
ans=min(ans,dis[n][i]);
}
cout<<ans;
}
int main()
{
freopen("step.in","r",stdin);
freopen("step.out","w",stdout);
init();
work();
}
注:用DP也可以A掉这道题