USACO20 JAN&FEB P

JAN 的 T3 too hard for me!

P6008 [USACO20JAN]Cave Paintings P

考虑水位不断上涨的过程即每次合并若干个列(点),注意到合并的答案等于 2 者的乘积,并查集维护一下即可。

#include <bits/stdc++.h> #define int long long #define pb push_back #define ID(x,y) (((x)-1)*m+(y)) using namespace std; const int N=1005; const int mod=(int)(1e9+7); char e[N][N]; int n,m,tot,fa[N*N],f[N*N]; int fd(int x) { return x==fa[x]?x:fa[x]=fd(fa[x]); } void merge(int x,int y) { x=fd(x); y=fd(y); if(x!=y) fa[x]=y,f[y]=f[y]*f[x]%mod; } signed main() { // freopen("6.in","r",stdin); cin.tie(0); ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>e[i][j]; if(e[i][j]=='.') f[ID(i,j)]=1; fa[ID(i,j)]=ID(i,j); } for(int i=n;i>=1;i--) { for(int j=1;j<=m;j++) { if(e[i][j]=='#') continue ; if(i+1<=n&&e[i+1][j]=='.') merge(ID(i+1,j),ID(i,j)); if(j-1>=1&&e[i][j-1]=='.') merge(ID(i,j-1),ID(i,j)); if(j+1<=m&&e[i][j+1]=='.') merge(ID(i,j+1),ID(i,j)); } for(int j=1;j<=m;j++) { if(e[i][j]=='#') continue ; if(fd(ID(i,j))==ID(i,j)) ++f[ID(i,j)]; } } int ans=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(e[i][j]=='#') continue ; if(fd(ID(i,j))==ID(i,j)) ans=ans*f[ID(i,j)]%mod; } } cout<<ans; return 0; }

P6009 [USACO20JAN]Non-Decreasing Subsequences P

类猫树分治,但不需要记录每一层,考虑记起点是啥,上一个是啥,从中间向两边即可。

#include <bits/stdc++.h> #define ll long long #define pb push_back #define ADD(x,y) ((x)+(y)>=mod?(x)+(y)-mod:(x)+(y)) using namespace std; const int mod=(int)(1e9+7),N=(int)(5e4+5),M=(int)(2e5+5); vector<pair<int,int> >vec[N]; int a[N],n,m,K,f[N][21][21],g[N][21][21],tmp[N],ans[M],val[N][22],val2[N][22]; void solve(int l,int r) { if(l==r) return ; int mid=(l+r)>>1; solve(l,mid); solve(mid+1,r); for(int i=l-1;i<=mid+1;i++) { memset(g[i],0,sizeof(g[i])); memset(val[i],0,sizeof(val[i])); } for(int i=mid;i>=l;i--) { for(int st=1;st<=K;st++) { for(int las=1;las<=K;las++) { g[i][st][las]=g[i+1][st][las]; } if(st==a[i]) ++g[i][st][a[i]]; for(int j=a[i];j<=K;j++) g[i][st][a[i]]=ADD(g[i][st][a[i]],g[i+1][st][j]); for(int j=1;j<=K;j++) val[i][st]=ADD(val[i][st],g[i][st][j]); } } for(int i=mid;i<=r+1;i++) { memset(f[i],0,sizeof(f[i])); memset(val2[i],0,sizeof(val2[i])); } for(int i=mid+1;i<=r;i++) { for(int st=1;st<=K;st++) { for(int las=1;las<=K;las++) { f[i][st][las]=f[i-1][st][las]; } if(st==a[i]) ++f[i][st][a[i]]; for(int j=1;j<=a[i];j++) f[i][st][a[i]]=ADD(f[i][st][a[i]],f[i-1][st][j]); for(int j=1;j<=K;j++) val2[i][st]=ADD(val2[i][st],f[i][st][j]); } } for(int i=mid+1;i<=r;i++) { while(!vec[i].empty()&&vec[i].back().first>=l) { int qwq=vec[i].back().first,id=vec[i].back().second; // cout<<l<<" "<<r<<" "<<i<<" "<<qwq<<'\n'; ans[id]=1; for(int j=1;j<=K;j++) for(int k=j;k<=K;k++) ans[id]=ADD(ans[id],1ll*val[qwq][j]*val2[i][k]%mod); for(int j=1;j<=K;j++) ans[id]=ADD(ans[id],ADD(val[qwq][j],val2[i][j])); vec[i].pop_back(); } } } signed main() { // freopen("22.in","r",stdin); // freopen("xgf.out","w",stdout); cin.tie(0); ios::sync_with_stdio(false); cin>>n>>K; for(int i=1;i<=n;i++) cin>>a[i]; cin>>m; for(int i=1;i<=m;i++) { int l,r; cin>>l>>r; if(l==r) { ans[i]=2; continue ; } vec[r].pb(make_pair(l,i)); } for(int i=1;i<=n;i++) sort(vec[i].begin(),vec[i].end()); solve(1,n); for(int i=1;i<=m;i++) cout<<ans[i]<<'\n'; return 0; }

P6142 [USACO20FEB]Delegation P

考虑类似赛道修建,二分,变为判定性问题。不难发现一个点的子树内至多只能剩下一条链,这条链再去与祖先链配对。于是考虑从小到大尝试两两配对(小的能配成功的链比较少,贪心 trick),若非根但当前有奇数条子树链,考虑剩下的一条即可。若有偶数条,其实也有可能一条链单独满了,变为奇数条的情况,因此你加一个 0 链即可。

注意,根一定要配完!所以倘若是奇数条链的话,必须存在一条自己能满足条件的。

#include <bits/stdc++.h> //#define int long long #define pb push_back using namespace std; const int N=(int)(1e5+5); vector<int>g[N]; int n,f[N],Lim; bool FL; void dfs(int x,int ff) { for(int y:g[x]) { if(y==ff) continue ; dfs(y,x); if(!FL) return ; } if(!FL) return ; multiset<int>s; s.clear(); for(int y:g[x]) { if(y==ff) continue ; s.insert(f[y]+1); } if(s.empty()) return ; if(x!=1&&((s.size())%2==0)) { s.insert(0); } if(x==1&&(s.size())&1) s.insert(0); bool ok=1; while(!s.empty()) { int qwq=(*s.begin()); s.erase(s.begin()); auto p=s.lower_bound(Lim-qwq); // cout<<qwq<<" "<<Lim<<'\n'; if(p==s.end()) { if(ok) { ok=0; f[x]=qwq; continue ; } else { FL=0; break ; } } else { s.erase(p); } } if(x==1&&!s.empty()) { FL=0; return ; } } bool chk(int x) { Lim=x; FL=1; dfs(1,0); return FL; } signed main() { // cin.tie(0); ios::sync_with_stdio(false); cin>>n; for(int i=1;i<n;i++) { int x,y; cin>>x>>y; g[x].pb(y); g[y].pb(x); } int l=1,r=n,res=0; while(l<=r) { int mid=(l+r)>>1; if(chk(mid)) res=mid,l=mid+1; else r=mid-1; } cout<<res; return 0; }

P6143 [USACO20FEB]Equilateral Triangles P

很厉害的题!

我一头栽进去转完切比雪夫距离后三角形的形状了。

image

考虑 A,B 所连直线 k 要么 1 要么 1,这是显然的。

你考虑转切比雪夫后画正方形,然后你会发现一定存在 2 个点所连边与 x 轴或者与 y 轴平行,然后你旋转 π4 回去显然。

然后就是一个斜向前缀和了,注意下算 "2+1",即这 4 个端点选 2 个组合上一个非端点的,再算 "3" 的。

#include <bits/stdc++.h> #define pb push_back #define ll long long #define int ll using namespace std; int a[605][605]; int n; ll ans,sum1[605][605],sum2[1205][1205],tmp[1205][1205]; void sol1(int x,int y,int l) { int xx=x-l,yy=y-l; if(xx<1||yy<1) return ; if(!a[xx][y]||!a[x][yy]) return ; // for(int i=1;i<l;i++) { // int nwy=y+i,nwx=l-i+x; // if(nwx<1||nwy<1||nwx>n||nwy>n) continue ; // if(a[nwx][nwy]) ++ans; // } // [l-1+x,1+x] [y+1,y+l-1] // int nwx=x+1,nwy=y+l-1; // while(nwx<=x+l-1&&nwy>0) { // ans+=a[nwx][nwy]; // ++nwx; --nwy; // } if(1<l) ans+=sum2[x+l-1][y+1]-sum2[x][y+l]; } void sol2(int x,int y,int l) { int xx=x-l,yy=y+l; if(xx<1||yy>n) return ; if(!a[xx][y]||!a[x][yy]) return ; if(1<=l-1) { int dwx=l-(l-1)+x-1,dwy=y-(l-1)-1; while(dwx<0||dwy<0) ++dwx,++dwy; if(dwx<0||dwy<0) { if(dwx<0&&dwy<0) { int qwq=max(-dwx,-dwy); dwx+=qwq; dwy+=qwq; } else if(dwx<0) { dwy+=-dwx; dwx=0; } else if(dwy<0) { dwx+=-dwy; dwy=0; } } ans+=sum1[l-1+x][y-1]-sum1[dwx][dwy]; } } void sol3(int x,int y,int l) { int xx=x+l,yy=y+l; if(xx>n||yy>n) return ; if(!a[xx][y]||!a[x][yy]) return ; // for(int i=1;i<l;i++) { // int nwy=y-i,nwx=x-l+i; // if(nwx<1||nwy<1||nwx>n||nwy>n) continue ; // if(a[nwx][nwy]) ++ans; // } // x[x-l+1,x-1] y[y-l+1,y-1] int nwx=x-l+1,nwy=y-1; while(nwx<=x-1) { if(nwx>=0&&nwy>=0) ans+=a[nwx][nwy]; ++nwx; --nwy; } // if(1<l) { // cout<<x-1+n<<" "<<y-l+1+n<<" "<<x-l+n<<" "<<y+n<<'\n'; // ans+=sum2[x-1+2*n][y-l+1+2*n]-sum2[x-l+2*n][y+2*n]; // } } void sol4(int x,int y,int l) { int xx=x+l,yy=y-l; if(xx>n||yy<1) return ; if(!a[xx][y]||!a[x][yy]) return ; if(1<=l-1) { int dwx=x-l,dwy=y; while(dwx<0||dwy<0) ++dwx,++dwy; if(dwx<0||dwy<0) { if(dwx<0&&dwy<0) { int qwq=max(-dwx,-dwy); dwx+=qwq; dwy+=qwq; } else if(dwx<0) { dwy+=-dwx; dwx=0; } else if(dwy<0) { dwx+=-dwy; dwy=0; } } ans+=sum1[x-1][y+l-1]-sum1[dwx][dwy]; } } void sol(int x,int y,int xx,int yy,int xxx,int yyy) { if(x<1||y<1||xx<1||yy<1||x>n||y>n||xx>n||yy>n||xxx<1||yyy<1||xxx>n||yyy>n) return ; ans+=((a[x][y]&a[xx][yy])&(a[xxx][yyy])); } signed main() { cin.tie(0); ios::sync_with_stdio(false); cin>>n; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { char ch; cin>>ch; if(ch=='*') a[i][j]=1; } } for(int i=1;i<=2*n;i++) { for(int j=1;j<=2*n;j++) { sum1[i][j]=sum1[i-1][j-1]+a[i][j]; } for(int j=2*n;j>=1;j--) { sum2[i][j]=sum2[i-1][j+1]+a[i][j]; } } // for(int i=1;i<=2*n;i++) // for(int j=1;j<=2*n;j++) // sum2[i+2*n][j+2*n]=tmp[i][j]; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { // if(!a[i][j]) continue ; for(int k=1;k<=n;k++) { sol1(i,j,k); sol2(i,j,k); sol3(i,j,k); sol4(i,j,k); int x=i,y=j; sol(x-k,y,x,y+k,x+k,y); sol(x,y+k,x+k,y,x,y-k); sol(x+k,y,x,y-k,x-k,y); sol(x,y-k,x-k,y,x,y+k); } } } cout<<ans; return 0; }

P6144 [USACO20FEB]Help Yourself P

学到了,你要让线段以某种顺序加入计算联通线段的段的个数,你可以按 l 排序,这样子每次加入一定不会合并之前分离的线段段的。

image

考虑一下为啥直接做二项式定理是对的。

你考虑一个状态的 dp 值可能是 iSlik 的形式的,然后你加 1 了,那么变为 iS(li+1)k,注意一下二项式定理,即 iSj(kj)lij,然后你提出去,j(kj)iSlij,记 f(S,i)xSlxi,则前者变为 j(kj)f(S,j),所以我们直接对答案做一次二项式定理是对的。综上,你只需要维护这个多项式 [0,k] 次的答案即可。

#include <bits/stdc++.h> #define pb push_back #define int long long #define ADD(x,y) ((x)+(y)>=mod?(x)+(y)-mod:(x)+(y)) using namespace std; const int mod=(int)(1e9+7); const int N=(int)(2e5+5); struct Li { int l,r; }p[N]; int n,K,jie[20],djie[20]; struct node { int a[12]; node() { memset(a,0,sizeof(a)); } void clr() { memset(a,0,sizeof(a)); } }f[N],g[N]; int C(int n,int m) { if(n<0||m<0||m>n) return 0; return jie[n]*djie[m]%mod*djie[n-m]%mod; } int fpow(int x,int y) { int res=1; x%=mod; while(y) { if(y&1) res=res*x%mod; y>>=1; x=x*x%mod; } return res; } node add(const node &x,const node &y) { node res; for(int i=0;i<=K;i++) res.a[i]=ADD(x.a[i],y.a[i]); return res; } node add1(const node &x) { node res; for(int i=0;i<=K;i++) { for(int j=0;j<=i;j++) { res.a[i]=ADD(res.a[i],x.a[j]*C(i,j)%mod); } } return res; } node mul(const node &x,int pp) { node res; int qwq=fpow(2,pp); for(int i=0;i<=K;i++) res.a[i]=x.a[i]*qwq%mod; return res; } bool cmp(const Li &x,const Li &y) { return x.l==y.l?x.r<y.r:x.l<y.l; //按 l 是因避免加入后使前面的联通段合并起来 } #define ls ((cur)<<1) #define rs ((ls)|1) node sum[N<<3]; int tag[N<<3]; void push_up(int cur) { sum[cur]=add(sum[ls],sum[rs]); } void build(int cur,int l,int r) { if(l==r) { sum[cur]=f[l]; return ; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); push_up(cur); } void push_down(int cur) { if(!tag[cur]) return ; tag[ls]+=tag[cur]; tag[rs]+=tag[cur]; sum[ls]=mul(sum[ls],tag[cur]); sum[rs]=mul(sum[rs],tag[cur]); tag[cur]=0; } void upt1(int cur,int l,int r,int p,const node &ad) { if(l==r) { sum[cur]=add(sum[cur],ad); return ; } push_down(cur); int mid=(l+r)>>1; if(p<=mid) upt1(ls,l,mid,p,ad); else upt1(rs,mid+1,r,p,ad); push_up(cur); } void upt2(int cur,int l,int r,int cl,int cr) { if(cl<=l&&r<=cr) { ++tag[cur]; sum[cur]=mul(sum[cur],1); return ; } push_down(cur); int mid=(l+r)>>1; if(cl<=mid) upt2(ls,l,mid,cl,cr); if(cr>mid) upt2(rs,mid+1,r,cl,cr); push_up(cur); } node qry(int cur,int l,int r,int cl,int cr) { if(cl<=l&&r<=cr) return sum[cur]; push_down(cur); int mid=(l+r)>>1; if(cr<=mid) return qry(ls,l,mid,cl,cr); else if(cl>mid) return qry(rs,mid+1,r,cl,cr); else return add(qry(ls,l,mid,cl,cr),qry(rs,mid+1,r,cl,cr)); } signed main() { cin.tie(0); ios::sync_with_stdio(false); cin>>n>>K; jie[0]=djie[0]=1; for(int i=1;i<20;i++) jie[i]=jie[i-1]*i%mod,djie[i]=fpow(jie[i],mod-2); for(int i=1;i<=n;i++) cin>>p[i].l>>p[i].r; sort(p+1,p+1+n,cmp); f[0].a[0]=1; build(1,0,2*n); for(int i=1;i<=n;i++) { int l=p[i].l,r=p[i].r; node qwq; if(l<=r-1) qwq=qry(1,0,2*n,l,r-1),upt1(1,0,2*n,r,qwq); if(r+1<=2*n) upt2(1,0,2*n,r+1,2*n); qwq=qry(1,0,2*n,0,l-1); qwq=add1(qwq); upt1(1,0,2*n,r,qwq); // for(int j=0;j<=2*n;j++) g[j]=f[j],f[j].clr(); // for(int j=l;j<=r;j++) f[r]=add(f[r],g[j]); // node tmp; // for(int j=l;j<r;j++) f[j]=g[j]; // for(int j=r+1;j<=2*n;j++) f[j]=mul2(g[j]); // for(int i=0;i<l;i++) tmp=add(tmp,g[i]),f[i]=g[i]; // tmp=add1(tmp); f[r]=add(f[r],tmp); } int ans=0; for(int i=1;i<=2*n;i++) { node qwq=qry(1,0,2*n,i,i); ans=ADD(ans,qwq.a[K]); } cout<<ans; return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/16792056.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示