2020.02.05【NOIP提高组】模拟A 组
[toc] CF293B Distinct Paths=JZOJ 4012 CF261E Maxim and Calculator=JZOJ 4010
JZOJ 2292 PPMM
题目
满足队列出入队,还要全部取反或者输出队列最大数
分析
首先全部取反也就是输出队列最小值的相反数
所以最理想的方法就是单调队列
也可以用带$log$的数据结构维护,但是容易被卡掉,
这里用树状数组,但是树状数组不满足删除,所以就把下标旋转,跑的还是挺快的
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=2000011,inf=2147483647;
int n,cur,now,op,ges,cmin[N],cmax[N];
inline signed iut(){
rr int ans=0,f=1; rr char c=getchar();
while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans*f;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void Min(int &x,int y){if (x>y) x=y;}
inline void Max(int &x,int y){if (x<y) x=y;}
inline void add(int x,int y){for (;x<=now;x+=-x&x) Min(cmin[x],y),Max(cmax[x],y);}
inline signed qmin(int x){
rr int ans=inf;
for (;x;x-=-x&x) Min(ans,cmin[x]);
return ans;
}
inline signed qmax(int x){
rr int ans=-inf;
for (;x;x-=-x&x) Max(ans,cmax[x]);
return ans;
}
inline void Pop(){if (cur<=now) --now;}
inline void Push(int x){add(--cur,x*op);}
inline void Fan(){if (cur<=now) op*=-1,ges^=1;}
inline void Gax(){
if (cur<=now){
rr int ans=op*(ges?qmin(now):qmax(now));
if (ans<0) putchar('-'),print(-ans);
else print(ans);
putchar(10);
}
}
signed main(){
n=iut(),cur=n+1,now=n,op=1;
for (rr int i=1;i<=n;++i) cmin[i]=inf,cmax[i]=-inf;
for (rr int i=1;i<=n;++i){
rr char c=getchar(); rr bool flag=0;
while (c!='P'&&c!='M') c=getchar();
switch (c=getchar()){
case 'O':Pop(),flag=1;break;
case 'U':Push(iut()); break;
case 'I':Fan(); break;
case 'A':Gax(); break;
}
if (flag) c=getchar();
}
return 0;
}
JZOJ 4012 Distinct Paths
题目
有一个$n*m$的木板,一些块已经被涂上给出的$k$种颜色中的一种。
你需要把每个没涂色的块涂色使得从左上角到右下角的每条路径都不会经过两个颜色一样的块。路径只能向右或向下走。
分析
首先这道题就是$n+m-1>k$肯定不可能的,所以其实就是搜索,但是要有技巧的剪枝
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int mod=1000000007;
int g[11][11],a[11][11];
int cnt[11],xo[1024],n,m,k;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline signed dfs(int x,int y){
if (y==m+1) ++x,y=1;
if (x==n+1) return 1;
rr int S=g[x-1][y]|g[x][y-1],ans=0,sing=-1;
if (n+m-x-y+1>k-xo[S]) return 0;
for (rr int i=0;i<k;++i)
if (!(S&(1<<i))&&((!a[x][y])||(a[x][y]==i+1))){
++cnt[i+1],g[x][y]=S|(1<<i);
if (cnt[i+1]==1){
if (sing==-1) sing=dfs(x,y+1);
ans=mo(ans,sing);
}else ans=mo(ans,dfs(x,y+1));
--cnt[i+1];
}
return ans;
}
signed main(){
for (rr int i=1;i<1024;++i) xo[i]=xo[i&(i-1)]+1;
n=iut(),m=iut(),k=iut();
if (n+m>k+1) return !putchar(48);
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=m;++j) ++cnt[a[i][j]=iut()];
return !printf("%d",dfs(1,1));
}
JZOJ 4010 Philips and Calculator
题目
可以用$x$次操作后选取$y$次$1\sim x$的可以相同的数相乘,但要保证$x+y\leq p$,问$[l\sim r]$有多少数能够这样被得到
分析
首先$x<p$所以可以把$2\sim p-1$的质数预处理,然后用这些数构造$1\sim r$的能够被乘出来的数进行dp
赛时想到应该是质因数个数+最大质因数,然而并不是,就以$64=26=43=8^2$为例,选择$4+3=7$答案更小
所以就$dp$,设$dp[n]$表示$n$所需要表示的最小步数,那么$dp[n]=\min{dp[n/i]+1}$,时间复杂度$O(pn)$,还要注意优化
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int prime[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
const int N=3000011; int l,r,p,ans,tot,bac,dp[N],a[N]; bool v[N];
inline void dfs(int dep,int now){
a[++tot]=now;
for (rr int i=dep;i<=bac;++i)
if (now<=r/prime[i])
dfs(i,now*prime[i]);
}
signed main(){
scanf("%d%d%d",&l,&r,&p);
if (p==1||p==2) return !putchar(48);
for (bac=24;prime[bac]>=p;--bac);
dfs(0,1),sort(a+1,a+1+tot),v[1]=1;
for (rr int i=2;i<=tot;++i) dp[i]=1e9;
for (rr int i=2;i<=p;++i)
for (rr int k=1,j=i;k<=tot;++k){
if (dp[k]+i+1>p) continue;
for (;j<=tot&&a[j]!=a[k]*i;++j);
if (j>tot) break;
if (dp[j]>dp[k]+1) dp[j]=dp[k]+1;
if (a[j]<l||v[j]||dp[j]+i>p) continue;
++ans,v[j]=1;
}
return !printf("%d",ans);
}