HZOI20190818模拟25题解
题面:https://www.cnblogs.com/Juve/articles/11372379.html
A:字符串
其实是CATALAN数水题。。。
和网格一毛一样:https://www.cnblogs.com/Juve/p/11222358.html
#include<iostream> #include<cstdio> #include<cstring> #define mod 20100403 #define int long long using namespace std; int n,m,ans=0; int q_pow(int a,int b,int p){ int res=1; while(b){ if(b&1) res=(res*a)%p; a=(a*a)%p; b>>=1; } return res%p; } int fac(int n){ if(n==1||n==0) return 1; int res=1; for(int i=2;i<=n;i++) res=(res*i)%mod; return res%mod; } int inv(int n){ if(n==0) return 1; return q_pow(fac(n),mod-2,mod)%mod; } int C(int n,int m){ if(n==m) return 1; if(n<m) return 0; return fac(n)*inv(m)%mod*inv(n-m)%mod; } signed main(){ scanf("%lld%lld",&n,&m); ans=C(n+m,n)*q_pow(m+1,mod-2,mod)%mod*(n-m+1)%mod; printf("%lld\n",ans%mod); return 0; }
B:乌鸦坐飞机喝水
乌鸦喝水是“能喝的时候一定喝”的,所以和DP决策没关系。
直接模拟的复杂度为O(nm)
把每个水缸按照可以喝水的次数(即喝多少次水,水位下降到喝不到)由小到大排序,依次处理。
显然如果前面的水缸能喝n次,后面的水缸至少也能喝n次,这个性质十分有用。
由上面这句话可以推出:如果乌鸦从当前位置飞到队伍末会经过k个能喝的水缸,而当前水缸还能喝的次数x>=k,那么之后的水缸也不会在这一轮被喝空。
1、枚举每一个水缸开始(在枚举到该水缸的时候,前面剩余次数更小的水缸已经被喝完。或者可以理解为,舍弃前面的缸,贪心喝这个。)
2、用树状数组维护水缸的ID,查询从当前水缸的ID到(原先的)第n个水缸还有多少个能喝的水缸,如果当前缸的剩余次数>=剩余缸数,直接更新次数并跳到下一轮。
3、在数次2操作后,当前缸的剩余次数<=剩余缸数,此时在树状数组上二分“余数”,即喝到又一轮的某位置时,当前缸没水了。
4、将当前缸标为空,枚举下一个水缸。
表面看着复杂度高,实际上喝水的次数不会超过最多的那个缸能喝的次数(因为每次喝水都是所有缸水位一起下降的),同时,模拟操作的次数不会超过m(最多喝m轮),复杂度可以接受。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define int long long #define MAXN 100005 using namespace std; int n,m,x; struct node{ int d,a,id,tim; friend bool operator < (node a,node b){ return a.tim<b.tim; } }w[MAXN]; struct BITREE{ int c[MAXN]; int lowbit(int x){ return x&(-x); } void update(int pos,int val){ for(int i=pos;i<=n;i+=lowbit(i)) c[i]+=val; } int query(int pos){ int res=0; for(int i=pos;i;i-=lowbit(i)) res+=c[i]; return res; } }BIT; int last=0,now=0,ans=0; int find(int x){ int res=last,l=last,r=n; while(l<=r){ int mid=(l+r)>>1; if(BIT.query(mid)-BIT.query(last)<=x) res=mid,l=mid+1; else r=mid-1; } return res; } signed main(){ scanf("%lld%lld%lld",&n,&m,&x); for(int i=1;i<=n;i++){ scanf("%lld",&w[i].d); w[i].id=i; } for(int i=1;i<=n;i++){ scanf("%lld",&w[i].a); w[i].tim=(x-w[i].d)/w[i].a+1; BIT.update(w[i].id,1); } sort(w+1,w+n+1); for(int i=1;i<=n;i++){ if(w[i].tim<ans){ BIT.update(w[i].id,-1); continue; } while(now<m){ int sum=BIT.query(n)-BIT.query(last); if(sum+ans>w[i].tim) break; ans+=sum; last=0; now++; } if(now>=m) break; last=find(w[i].tim-ans); ans=w[i].tim; BIT.update(w[i].id,-1); } printf("%lld\n",ans); return 0; }
C:所驼门王的宝藏
%%AlpaCa羊驼哥
题面挺吓人,但是水题一个
首先你要从提干中的输入建出图来
建图非常sb
我定义了两个vector,存每行每列的宫殿坐标及类型
然后扫描每行,搜到一个横天门就把它和这一行每一个宫殿连边,其他同理,
然后就和之前的爆炸行动一样了,
tarjan缩点,然后在新建的图上跑拓扑,找到新图上的最长链
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> #include<queue> #define MAXK 1000005 #define re register using namespace std; int n,m,k,ans=0,res[MAXK]; struct node{ int opt,id,x,y; }; vector<node>h[MAXK],l[MAXK]; struct EDGE{ int to,nxt; }e[MAXK<<2]; int head[MAXK],cnt=0; inline void add(re int u,re int v){ cnt++,e[cnt].to=v,e[cnt].nxt=head[u],head[u]=cnt; } int dfn[MAXK],low[MAXK],sta[MAXK],top=0,dfs_order=0,tot=0,belong[MAXK],siz[MAXK]; bool in_sta[MAXK]; inline void tarjan(re int x){ dfn[x]=low[x]=++dfs_order; sta[++top]=x; in_sta[x]=1; for(re int i=head[x];i;i=e[i].nxt){ re int y=e[i].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(in_sta[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ tot++; re int y; do{ y=sta[top--]; in_sta[y]=0; belong[y]=tot; siz[tot]++; }while(y!=x); } } int to[MAXK<<2],nxt[MAXK<<2],pre[MAXK],sum=0; inline void ADD(re int u,re int v){ sum++,to[sum]=v,nxt[sum]=pre[u],pre[u]=sum; } int in_deg[MAXK]; queue<int>q; int main(){ scanf("%d%d%d",&k,&n,&m); for(re int i=1,u,v,opt;i<=k;i++){ scanf("%d%d%d",&u,&v,&opt); h[u].push_back((node){opt,i,u,v}); l[v].push_back((node){opt,i,u,v}); } for(re int i=1;i<=n;i++){ re int N=h[i].size(); for(re int j=0;j<N;j++){ node t=h[i][j]; if(t.opt==1){ for(re int p=0;p<N;p++){ if(t.id==h[i][p].id) continue; add(t.id,h[i][p].id); } }else if(t.opt==2){ re int M=l[t.y].size(); for(re int p=0;p<M;p++){ if(t.id==l[t.y][p].id) continue; add(t.id,l[t.y][p].id); } }else{ for(re int p=max(1,t.x-1);p<=min(n,t.x+1);p++){ re int r=h[p].size(); for(re int q=0;q<r;q++){ if(h[p][q].id==t.id) continue; if(abs(t.y-h[p][q].y)>1) continue; add(t.id,h[p][q].id); } } } } } for(re int i=1;i<=k;i++){ if(!dfn[i]) tarjan(i); } for(re int i=1;i<=k;i++){ for(re int j=head[i];j;j=e[j].nxt){ re int y=e[j].to; if(belong[y]!=belong[i]){ ADD(belong[i],belong[y]); in_deg[belong[y]]++; } } } for(re int i=1;i<=tot;i++){ res[i]=siz[i]; if(!in_deg[i]) q.push(i); } while(!q.empty()){ re int x=q.front(); q.pop(); in_deg[x]--; for(re int i=pre[x];i;i=nxt[i]){ re int y=to[i]; in_deg[y]--; res[y]=max(res[y],res[x]+siz[y]); ans=max(res[y],ans); if(!in_deg[y]) q.push(y); } } //for(re int i=1;i<=tot;i++) // ans=max(res[i],ans); printf("%d\n",ans); return 0; }