一个0到n-1的环,初始在0,每次随机前进或后退一格,当全部点都访问过就结束,问结束时站在k的几率是多少。(注意输出的是前缀积)node
一开始站在0,最后显然不可能在0,剩下n-1格,随机数打表发现几率相同,都为\(\frac{1}{n-1}\)c++
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; int cnt[1050]; bool vis[1050]; void sui(int n){ memset(cnt,0,sizeof(cnt)); int num=100000; while(num--){ memset(vis,false,sizeof(vis)); vis[0]=true; int now=0; while(true){ bool flag=true; for(int i=0;i<n;i++){ if(!vis[i]){ flag=false; } } if(flag){ break; } int rn=rand()%10; if(rn<5){ now=(now-1+n)%n; }else{ now=(now+1)%n; } vis[now]=true; } cnt[now]++; } for(int i=0;i<n;i++){ printf("%d%c",cnt[i],i==n-1?'\n':' '); } } ll Pow(ll a,ll n){ ll ans=1ll; while(n){ if(n%2){ ans=ans*a%mod; } a=a*a%mod; n/=2;; } return ans; } int t,n,m; int main(){ scanf("%d",&t); ll ans=1ll; while(t--){ scanf("%d%d",&n,&m); ll tmp; if(m==0){ if(n==1){ tmp=1ll; }else{ tmp=0; } }else{ tmp=Pow(1ll*(n-1),mod-2); } ans=(ans*tmp)%mod; printf("%lld\n",ans); } }
无限长的路,每次能够向前走1-k步,几率相同,问走到n的几率。算法
相似于经典dp走楼梯,设dp[i]为走到i的几率,那么\(dp[i]=\frac{1}{k}\sum_{j=i-k}^{i-1}dp[j]\),这是一种线性递推的关系,因此求出前几项,而后用BM算法求出第n项。ui
当n为无穷大的状况,先计算走一步的指望,\(\sum_{i=1}^k\frac{i}{k}=\frac{k+1}{2}\),那么反过来,走到某一格的几率就是\(\frac{1}{\frac{k+1}{2}}=\frac{2}{k+1}\)spa
#include <bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second #define SZ(x) ((int)(x).size()) typedef long long ll; typedef vector<ll> VI; typedef pair<int,int> PII; const ll mod=1000000007; ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} // head ll n; namespace linear_seq { const int N=10010; ll res[N],base[N],_c[N],_md[N]; vector<int> Md; void mul(ll *a,ll *b,int k) { rep(i,0,k+k) _c[i]=0; rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod; for (int i=k+k-1;i>=k;i--) if (_c[i]) rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod; rep(i,0,k) a[i]=_c[i]; } int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+... ll ans=0,pnt=0; int k=SZ(a); assert(SZ(a)==SZ(b)); rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1; Md.clear(); rep(i,0,k) if (_md[i]!=0) Md.push_back(i); rep(i,0,k) res[i]=base[i]=0; res[0]=1; while ((1ll<<pnt)<=n) pnt++; for (int p=pnt;p>=0;p--) { mul(res,res,k); if ((n>>p)&1) { for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0; rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod; } } rep(i,0,k) ans=(ans+res[i]*b[i])%mod; if (ans<0) ans+=mod; return ans; } VI BM(VI s) { VI C(1,1),B(1,1); int L=0,m=1,b=1; rep(n,0,SZ(s)) { ll d=0; rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod; if (d==0) ++m; else if (2*L<=n) { VI T=C; ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; L=n+1-L; B=T; b=d; m=1; } else { ll c=mod-d*powmod(b,mod-2)%mod; while (SZ(C)<SZ(B)+m) C.pb(0); rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod; ++m; } } return C; } int gao(VI a,ll n) { VI c=BM(a); c.erase(c.begin()); rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod; return solve(n,c,VI(a.begin(),a.begin()+SZ(c))); } }; vector<ll> v; int t,k; ll dp[2050]; int main(void){ // freopen("in.txt","r",stdin); scanf("%d",&t); while(t--){ scanf("%d%lld",&k,&n); ll invk=powmod(k,mod-2); memset(dp,0,sizeof(dp)); dp[0]=1; v.clear(); v.push_back(1); for(int i=1;i<=2*k;i++){ dp[i]=0; for(int j=max(0,i-k);j<i;j++){ dp[i]=(dp[i]+dp[j])%mod; } dp[i]=(dp[i]*invk)%mod; // printf("%lld\n",dp[i]); v.push_back(dp[i]); } ll ans; if(n==-1){ ll invk_1=powmod(k+1,mod-2); ans=2ll*invk_1%mod; }else{ ans=linear_seq::gao(v,n); } printf("%lld\n",ans); } return 0; }
给一个图,求第k小团(彻底子图)。code
用bitset状压表示当前团,而后每次取出最小的,尝试加入一个点,知足该点和团内全部点都有边相连,用位运算实现,第k个取出的即为第k小团。it
为了不重复,标记每一个团是由那个点扩大而来的。io
#include <bits/stdc++.h> using namespace std; const int N=105; typedef long long ll; typedef bitset<N> bs; int n,k; ll a[N]; char s[N]; struct node{ int id; ll val; //当前团的状态 bs sta; bool operator <(const node& rhs)const{ return val>rhs.val; } }; bs g[N]; bs t; priority_queue<node> pq; int main(void){ // freopen("in.txt","r",stdin); scanf("%d%d",&n,&k); for(int i=0;i<n;i++){ scanf("%lld",&a[i]); } for(int i=0;i<n;i++){ scanf("%s",s); for(int j=0;j<n;j++){ g[i][j]=s[j]-'0'; } } if(k==1){ printf("0\n"); return 0; } k--; for(int i=0;i<n;i++){ t.reset(); t[i]=1; pq.push(node{i,a[i],t}); } while(!pq.empty()){ node tmp=pq.top(); pq.pop(); if(k==1){ printf("%lld\n",tmp.val); return 0; } k--; for(int i=tmp.id+1;i<n;i++){ //若是一个点所连的全部点(g[i]恰好是当前团的全部点sta,那么加入i后仍然是团) if((g[i]&tmp.sta)==tmp.sta){ bs now=tmp.sta; now[i]=1; pq.push(node{i,tmp.val+a[i],now}); } } } printf("-1\n"); return 0; }
给一个01矩阵,能够往左,右,下走,且只有0能够走,两种操做,修改某个点状态,询问从(1,a)到(n,b)的方案数。class
定义状态,\(dp[i][j]\)表示从\(i-1\)行通过\(g[i-1][j]\)走到\(g[i][j]\)的方案数,所以\(dp[i][j]=\sum_{j=i}^{r}dp[i-1][j]+\sum_{j=i-1}^{l}dp[i-1][j]\),其中\(l\)和\(r\)表示\(g[i-1][j]\),左右连续0延伸的位置。cli
因此答案显然就是\(dp[n+1][b]\),由于\(dp[n][b]\)只考虑了从\(g[n-1][b]\)往下走的方案数。
显然从第一层到第二层状态的转移是一个线性递推的式子,中间能够构造一个矩阵,那么从第一层到第n+1层的状态显然就是n个矩阵的乘积了,所求结果就是\((\sum_{i=1}^nM_i)[a][b]\)。
修改的操做直接修改矩阵,而后线段树单点修改便可。
#include <bits/stdc++.h> using namespace std; #define ls i<<1 #define rs i<<1|1 #define mid (l+r)/2 typedef long long ll; const int N=50050; const int M=12; const ll mod=1e9+7; int n,m,q,o,a,b; int g[N][M]; char s[M]; struct Mat{ ll m[M][M]; void init(){ memset(m,0,sizeof(m)); } }; Mat mMul(Mat a,Mat b){ Mat ans; ans.init(); for(int i=0;i<M;i++){ for(int j=0;j<M;j++){ ans.m[i][j]=0; for(int k=0;k<M;k++){ ans.m[i][j]+=(a.m[i][k]*b.m[k][j]%mod); ans.m[i][j]%=mod; } } } return ans; } //线段树维护区间矩阵连乘 Mat pro[N*4]; void pushup(int i){ pro[i]=mMul(pro[ls],pro[rs]); } void build(int i,int l,int r){ if(l==r){ //先构造出每一层的转移矩阵 // pro[i].init(); for(int j=1;j<=m;j++){ //g上一行连续0的部分在转移矩阵中为1 for(int k=j;k<=m;k++){ if(g[l][k]==0){ pro[i].m[j][k]=1; }else{ break; } } for(int k=j;k>=1;k--){ if(g[l][k]==0){ pro[i].m[j][k]=1; }else{ break; } } } return; } build(ls,l,mid); build(rs,mid+1,r); pushup(i); } void update(int i,int l,int r,int a,int b){ if(l==r && a==l){ //单点更新,重置转移矩阵 pro[i].init(); for(int j=1;j<=m;j++){ //g上一行连续0的部分在转移矩阵中为1 for(int k=j;k<=m;k++){ if(g[a][k]==0){ pro[i].m[j][k]=1; }else{ break; } } for(int k=j;k>=1;k--){ if(g[a][k]==0){ pro[i].m[j][k]=1; }else{ break; } } } return; } if(a<=mid){ update(ls,l,mid,a,b); }else{ update(rs,mid+1,r,a,b); } pushup(i); } int main(void){ // freopen("in.txt","r",stdin); scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++){ scanf("%s",s+1); for(int j=1;j<=m;j++){ g[i][j]=(s[j]-'0'); } } build(1,1,n); //对于每个查询 答案就是(\prod_{i-1}^n Mi)[a][b] while(q--){ scanf("%d%d%d",&o,&a,&b); if(o==1){ g[a][b]^=1; update(1,1,n,a,b); }else if(o==2){ printf("%lld\n",pro[1].m[a][b]%mod); } } return 0; }
将2n我的分为两队,给定一个n*n的权值矩阵,定义总权值为两队各n我的分别的权值和,求最大总权值。
直接暴力搜索,每加入一个就算贡献,不要等到所有分完再计算。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll g[30][30]; int n; ll ans; // vector<int> a,b; int a[30],b[30]; int ac,bc; void dfs(int idx,ll now){ if(ac==n && bc==n){ ans=max(ans,now); return; } if(ac<n){ ll t=0; for(int i=0;i<bc;i++){ t+=g[idx][b[i]]; } now+=t; a[ac++]=idx; dfs(idx+1,now); a[--ac]=0; now-=t; } if(bc<n){ ll t=0; for(int i=0;i<ac;i++){ t+=g[idx][a[i]]; } now+=t; b[bc++]=idx; dfs(idx+1,now); b[--bc]=0; now-=t; } } int main(void){ scanf("%d",&n); for(int i=0;i<2*n;i++){ for(int j=0;j<2*n;j++){ scanf("%lld",&g[i][j]); } } dfs(0,0); printf("%lld\n",ans); return 0; }
给一个01矩阵,求第二大的全1子矩阵。
和最大的全1子矩阵相似,对于每一行,咱们维护每一个点能往上延伸的最大长度,而后就至关于经典的单调栈题目,给一些不一样高度的柱子,求最大面积。
咱们只须要用单调栈维护每一个高度做为最小值能延伸到的最左和最右,而后扫一遍就能求出最大矩阵大小,可是这题要求的是次大,因此咱们不能单独考虑最大的矩阵,也就是不能求出次大的最大子矩阵,而是要把每一个最大子矩阵长减1,和宽减1再加入更新。
#include <bits/stdc++.h> using namespace std; const int N=1050; const int INF=0x3f3f3f3f; int h[N],le[N],ri[N]; int n,m; char s[N][N]; struct node{ int l,r,h; bool operator <(const node &rhs)const{ if(l!=rhs.l){ return l<rhs.l; }else{ if(r!=rhs.r){ return r<rhs.r; }else{ return h<rhs.h; } } } }; map<node,int> mp; stack<int> ss; int mx,se; void update(int x){ if(x>=mx){ se=mx; mx=x; }else if(x>se){ se=x; } } int main(void){ // freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%s",s[i]+1); } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(s[i][j]=='1'){ h[j]++; }else{ h[j]=0; } } while(!ss.empty()){ ss.pop(); } for(int j=1;j<=m;j++){ while(ss.size()>0 && h[j]<=h[ss.top()]){ ss.pop(); } if(ss.size()>0){ le[j]=ss.top()+1; }else{ le[j]=1; } ss.push(j); } while(!ss.empty()){ ss.pop(); } for(int j=m;j>=1;j--){ while(ss.size()>0 && h[j]<=h[ss.top()]){ ss.pop(); } if(ss.size()>0){ ri[j]=ss.top()-1;; }else{ ri[j]=m; } ss.push(j); } for(int j=1;j<=m;j++){ if(h[j]==INF){ continue; } if(mp[{le[j],ri[j],h[j]}]){ continue; } mp[{le[j],ri[j],h[j]}]=1; update(h[j]*(ri[j]-le[j]+1)); update(h[j]*(ri[j]-le[j])); update((h[j]-1)*(ri[j]-le[j]+1)); } } printf("%d\n",se); return 0; }