T1 字符串
卡特兰数
设1为向(1,1)走,0为向(1,-1)走,限制就是不能超过$y=0$这条线,题意转化为从(0,0)出发,走到(n+m,n-m)且不越过$y=0$,然后就裸的卡特兰数,$ans=C(n+m,n)-C(n+m,m-1)$
#include<iostream> #include<cstdio> #include<cstring> #define mod 20100403 #define ll long long using namespace std; ll n,m,inv[2001000],fac[2001000],facinv[2001000]; ll read() { ll aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } ll C(ll x,ll y) { return fac[x]*facinv[x-y]%mod*facinv[y]%mod; } int main() { n=read();m=read(); fac[0]=1;facinv[0]=1;inv[1]=1; for(ll i=1;i<=n+m;i++){ if(i!=1) inv[i]=(mod-mod/i)*inv[mod%i]%mod; fac[i]=fac[i-1]*i%mod; facinv[i]=facinv[i-1]%mod*inv[i]%mod; } printf("%lld\n",(C(n+m,n)-C(n+m,m-1)+mod)%mod); return 0; }
T2 乌鸦喝水
两条性质:
1.若水少的喝了$x$次,那么比他水多的至少喝了$x$次。所以按能喝的次数升序排列。
2.排序后的序列里,当前水缸还能喝的次数${\ge}k$,那么之后的水缸在这一轮也不会被喝完。
于是我们在排完序的序列里,每次找到最少的,看他能喝几轮,直接跳,所以用树状数组维护当前点的实际坐标的右边还可以喝$k$次,若$num>k+ans$那就飞一轮,同时更新答案,若$num<k+ans$,就在原序列上二分看他最多跳到哪(记得更新答案),同时把排序后的序列的下标向右推一位,因为这一位已经失效了。
树状数组的具体实现就是以原坐标为下标,插入每个点是否存在,存在为1,不存在为0,维护前缀和,查询的时候直接减就可以得到右边有多少个点。
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long using namespace std; struct node { int id,cnt; }h[100100]; ll n,m,x,w[100100],a[100100],c[100100],ans,cir; ll read() { ll aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } bool cmp(node a,node b) { return a.cnt==b.cnt?a.id<b.id:a.cnt<b.cnt; } int lowbit(int x) { return x&(-x); } void insert(int x,int w) { while(x<=n){ c[x]+=w; x+=lowbit(x); } } int query(int x) { int as=0; while(x){ as+=c[x]; x-=lowbit(x); } return as; } int main() { n=read();m=read();x=read(); for(int i=1;i<=n;i++) w[i]=read(); for(int i=1,u;i<=n;i++){ u=read(); h[i].id=i; h[i].cnt=(x-w[i])/u+1; insert(i,1); } sort(h+1,h+n+1,cmp); int pos=1,las=1;//pos排序后的序列 las原序列 for(int pos=1;pos<=n;pos++){ while(cir<m&&query(n)-query(las-1)+ans<h[pos].cnt){ cir++; ans+=query(n)-query(las-1); las=1; } if(cir>=m) break; int l=las,r=n,as=las-1; while(l<=r){ int mid=(l+r)>>1; if(query(mid)-query(las-1)+ans<=h[pos].cnt){ as=mid; l=mid+1; } else r=mid-1; } ans+=query(as)-query(las-1); las=(as==n)?1:as+1; if(as==n) cir++; insert(h[pos].id,-1); } printf("%lld\n",ans); return 0; }
T3 所驼门王的宝藏
考场看错题,周围8个点理解成向左右上下个拓展8个点,嘤嘤嘤
把每个传送门能去的有宝藏的地方建单向边,tarjan缩点,重新建边,topu找最长链。(因为是单向边,dfs是($n^2$)的,时间受不住,topu只需要($n^2$))
最恶心的是建边,用$vector$ 存每一行 每一列都有哪些宝藏(用于$opt==1||opt==2$),用$map$映射二元组记录每个点的位置(用于$opt==3$),然后直接建就行
#include<iostream> #include<cstdio> #include<vector> #include<map> #include<queue> using namespace std; struct node { int to,nxt; }h[10000010],hh[1000100]; struct nnde { int x,y,opt; }t[100100]; int ans; int n,r,c,tot,nxt[10000010],opt[100100],tx[9]={0,0,-1,-1,-1,1,1,1},ty[9]={-1,1,-1,0,1,-1,0,1}; int nx[100100],tet,d[100100],val[100100]; int dfn[100100],low[100100],s[100100],top,whos[100100],cnt,num,shu[100100]; bool is[100100],vis[100100],v[500100]; vector<int>hang[1001000],lie[1001000],ve; map< pair<int,int> ,int>mp; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } void add(int x,int y) { h[++tot].to=y; h[tot].nxt=nxt[x]; nxt[x]=tot; } void ad(int x,int y) { hh[++tet].to=y; hh[tet].nxt=nx[x]; nx[x]=tet; } void tarjan(int x) { dfn[x]=low[x]=++cnt; s[++top]=x;is[x]=1; for(int i=nxt[x];i;i=h[i].nxt){ int y=h[i].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(is[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]){ num++; while(1){ int tmp=s[top--]; whos[tmp]=num; shu[num]++; is[tmp]=0; if(tmp==x) break; } } } void topu() { queue<int>q; for(int i=1;i<=num;i++){ if(!d[i]) q.push(i),val[i]=shu[i],ans=max(ans,val[i]); } while(q.size()){ int x=q.front();q.pop(); for(int i=nx[x];i;i=hh[i].nxt){ int y=hh[i].to; d[y]--; val[y]=max(val[y],val[x]+shu[y]); ans=max(ans,val[y]); if(!d[y]) q.push(y); } } } int main() { n=read();r=read();c=read(); int x,y,opt; for(int i=1;i<=n;i++){ t[i].x=read();t[i].y=read();t[i].opt=read(); hang[t[i].x].push_back(i); lie[t[i].y].push_back(i); mp[make_pair(t[i].x,t[i].y)]=i; } for(int i=1;i<=n;i++){ int x=t[i].x,y=t[i].y,opt=t[i].opt; if(opt==1){ for(int j=0;j<hang[x].size();j++){ if(i==hang[x][j]) continue; add(i,hang[x][j]); } } else if(opt==2){ for(int j=0;j<lie[y].size();j++){ if(i==lie[y][j]) continue; add(i,lie[y][j]); } } else if(opt==3){ for(int j=0;j<8;j++){ if(mp[make_pair(x+tx[j],y+ty[j])]){ add(i,mp[make_pair(x+tx[j],y+ty[j])]); } } } } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++){ ve.clear(); for(int j=nxt[i];j;j=h[j].nxt){ int y=h[j].to; if(whos[y]!=whos[i]&&!vis[whos[y]]){ ad(whos[i],whos[y]); d[whos[y]]++; ve.push_back(whos[y]); vis[whos[y]]=1; } } for(int j=0;j<ve.size();j++) vis[ve[j]]=0; } topu(); printf("%d\n",ans); return 0; }