T组样例,每次有一个下标0到Ni-1的环,
每次可以随机选择向前一步或向后一步,0后一步是Ni-1,Ni-1向前一步是0
从下标为0的点开始随机游走,直到所有下标都被访问过一次之后停止
停止的位置是Mi,则称这组样例对应事件发生
问前i组样例对应事件连续发生的概率,分数mod 1e9+7
思路来源:官方题解
连续发生显然为前i次的概率相乘,考虑每次的概率
如果点数大于1,从0出发后,就不可能再走到0;所以n==1时m=1,n>1时m=0
对于其他的点,由于随机游走,最终在每个点停留的概率相同,为
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int modpow(int x,int n,int mod)
{
int ans=1;
for(;n;n/=2,x=1ll*x*x%mod)
if(n&1)ans=1ll*ans*x%mod;
return ans;
}
int t,n,m,ans;
int main()
{
scanf("%d",&t);
ans=1;
while(t--)
{
scanf("%d%d",&n,&m);
if(m==0)
{
if(n==1)ans=ans*1;
else ans=ans*0;
}
else ans=1ll*ans*modpow(n-1,mod-2,mod)%mod;
printf("%d\n",ans);
}
return 0;
}
T(1<=T<=10)组样例,第i次询问给出Ki(Ki<=1021)和Ni(-1<=Ni<=1e18)
一个人从0点出发,在数轴上向右走,
每次有1/K的概率向右走1步,有1/K的概率向右走2步,...,有1/K的概率向右走K步
问到达Ni点的概率是多少,Ni==-1时,代表Ni在无穷远处
①Ni==-1,也就是无穷远处
考虑走一步能走的距离的期望是,那么由于期望的性质,不妨认为每步都走
那么看能不能到达N,就等价于看是从0,1,...这个点哪个点出发的,
N只能从其中1个点出发到达,从而从这么多点选一个选中那个点的概率是,
群里请教大佬,严格证明需要中心极限定理,不懂,从略
放进杜教BM板子里搞一搞,预处理前2k项答案,搞出第N项取模mod答案
#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1024;
ll modpow(ll a,ll b,ll mod) {ll res=1;a%=mod; for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll inv(ll x){return modpow(x,mod-2,mod);}
namespace linear_seq {
#define rep(i,a,n) for (int i=a;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 vector VI;
typedef pair PII;
typedef long long ll;
const ll mod=1e9+7;
const int N=10010;
ll res[N],base[N],_c[N],_md[N];
ll modpow(ll a,ll b,ll mod) {ll res=1;a%=mod; for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
vector 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]+...
// printf("%d\n",SZ(b));
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<=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*modpow(b,mod-2,mod)%mod;
while (SZ(C)ans;
ll dp[2*N],p,n;
int t,k;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%lld",&k,&n);
if(n==-1)
{
printf("%lld\n",2*inv(k+1)%mod);//2/(k-1)
continue;
}
p=inv(k);
dp[0]=1;//dp[n]=1/k dp[n-1] +1/k dp[n-2]+...+1/k dp[n-k]
ans.clear();
ans.push_back(dp[0]);
for(int i=1;i<=2*k;++i)
{
dp[i]=0;
for(int j=max(i-k,0);j
当然也可以选择用的矩阵快速幂优化,
去求前k项的系数,抄了一下杜教的代码,加了点注释
#include
using namespace std;
#define rep(i,n) for(int i=1;i<=n;++i)
#define mp make_pair
#define pb push_back
#define x0 gtmsub
#define y0 gtmshb
#define x1 gtmjtjl
#define y1 gtmsf
typedef long long ll;
//M为递推项系数个数 c为系数数组
//最终递推式为a[m]=c[0]a[0]+c[1]a[1]+...+c[m-1]a[m-1]
const int M=1050,P=1000000007;
//求快速幂 只写一个系数即求逆元
ll pw(ll x,ll y=P-2){
ll s=1;
for(;y;y>>=1,x=1ll*x*x%P)
if(y&1)s=1ll*s*x%P;
return s;
}
ll i,w,x,b,j,t,a[M],c[M],v[M],u[M<<1],ans;
//推a1...ak每项最终系数的矩阵快速幂 复杂度O(k^2 logn)
//求a^n的k个a^1 a^k的系数 即求a^(n/2)的k个系数 然后乘在一起变成a^1 到a^(2k)的系数
//然后暴力把a^(k+1)到a^(2k)的系数再倒序下放到a^1 到 a^k上
ll sol(ll n,ll m) {//求a[n] a[n]来自前m项递推式
//scanf("%d%d",&n,&m);
n+=m-1;//整体右移m-1项 便于0-(m-1)向负的找系数 应为0
for(i=m-1;~i;i--)c[i]=pw(m);//c[m-1]到c[0] 每个1/m
for(i=0;i1;i>>=1)w<<=1;//n=0时w=0 n!=0时w>1 w为不大于n的最大2的次幂
for(x=0;w;copy(u,u+m,v),w>>=1,x<<=1){//copy把[u,u+m]复制给v
fill_n(u,m<<1,0),b=!!(n&w),x|=b;//fill_n 把u.begin()的连续m<<1个位置 都覆盖成0
//如果n&w==0 b=0;n&w==w b=1 用两个!把非空判成了1
//如果w最高位为1 则x最低位|=1
if (x=m;i--)for(j=0,t=i-m;j
N(N<=100)的图,第i个点上有点权wi(0<=wi<=1e9),
输出权值从小到大第K(1<=K<=1e6)的团的权值,空集算一个权值为0的团
思路来源:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40895304
这种做法应该不是标程做法,但也算能莽过去的做法吧
用bitset维护当前集合已选的点,和下一步能向集合加入的点,也就是维护团中边的信息
每次只向大的搜,保证必含第i个点的状态内的团的点为[i,...),从而不重复
复杂度:似乎是,但优先队列计一个清一个也到不了k,后者用bitset优化,极大地降了系数
#include
using namespace std;
typedef long long ll;
const int N=105;
int n,k,w[N];
struct node
{
bitsetnow;//当前已选点
bitsetnext;//已选点和哪些点都连通 只能从next集中找下一个点
ll sum;
int maxid;//当前搜到了哪个点
void clear()
{
now.reset();
next.reset();
sum=0;
maxid=0;
}
}e[N];
bool operator<(node a,node b)
{
return a.sum>b.sum;
}
bitsetmp[N];
priority_queueq;
char s[N];
ll solve()
{
k--;//空集也算一个
if(k==0)return 0;
for(int i=1;i<=n;++i)
{
e[i].clear();
e[i].now.set(i);
e[i].next=mp[i];
e[i].sum=w[i];
e[i].maxid=i;
q.push(e[i]);
}
while(k&&!q.empty())
{
node t=q.top();q.pop();
k--;
if(k==0)return t.sum;
for(int i=t.maxid+1;i<=n;++i)
{
if(t.next.test(i))
{
node v=t;
v.now.set(i);
v.next&=mp[i];
v.sum+=w[i];
v.maxid=i;
q.push(v);
}
}
}
if(k)return -1;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
for(int i=1;i<=n;++i)
mp[i].reset();
for(int i=1;i<=n;++i)
scanf("%d",&w[i]);
for(int i=1;i<=n;++i)
{
scanf("%s",s+1);
for(int j=1;j<=n;++j)
{
if(s[j]=='1')
mp[i].set(j);
}
}
printf("%lld\n",solve());
}
return 0;
}
官方题解:
二分权值maxV,判断<=maxV的团是否>=k个,找到最小的maxV
点权按从小到大排序,这样团转移到下一个团只需取lowbit对应的点,加上该点权值即可
复杂度,maxV<=n*maxwi=1e11,可惜WA得还没过,待补
给一个n*m(n<=5e4,m<=10)的01矩阵b,0是空地1是墙,
b[i][j]可以走到非墙的b[i+1][j]、b[i][j-1]和b[i][j+1],但不能回头
现有q个操作,操作分两种,每次给出x y z
1 x y 将b[x][y]取反
2 x y 询问b[1][x]到b[2][y]的路径方案数
思路来源:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40907986
线段树,叶子结点每个点维护一个m*m矩阵,
表示对于当前行l来说,哪两个点是连通的
考虑往上pushup的过程,就是通过跳板达到新的连通的过程
pushup一次时,a[i][j]*b[j][k]相当于,从第二行的i到第二行的j,再到第一行j,再到第一行k,方案唯一,
新的分之节点维护的是两行信息,...,依次类推,根节点是所有行的汇总信息,直接查询即可
更新时,更新叶子结点,然后完成pushup
#include
using namespace std;
const int N=5e4+10;
struct mat{
const static int MOD = 1e9+7;
const static int MAXN = 12;
int c[MAXN][MAXN];
int m, n;
mat(){
memset(c,0,sizeof (c));
}
mat(int a, int b) : m(a), n(b) {
memset(c, 0, sizeof(c));
}
void reset(int a,int b){
m=a; n=b;
}
void clear(){
memset(c, 0, sizeof(c));
}
mat operator * (const mat& temp) {
mat ans(m, temp.n);
for (int i = 1; i <= m; i ++)
for (int j = 1; j <= temp.n; j ++)
for (int k = 1; k <= n; k ++)
ans.c[i][j] = (ans.c[i][j] + 1ll * c[i][k] * temp.c[k][j] % MOD) % MOD;
return ans;
}
}dat[N*4];
int n,m,q,a[N][15],x,y,z;
char s[15];
void pushup(int p)
{
dat[p]=dat[p<<1]*dat[p<<1|1];
}
void op(int p,int l)
{
int pos;
dat[p].clear();
for(int i=1;i<=m;++i)
{
pos=i;
while(pos>=1&&a[l][pos]==0)
{
dat[p].c[i][pos]=1;//a[l][i]和a[l][pos]可达
pos--;
}
pos=i;
while(pos<=m&&a[l][pos]==0)
{
dat[p].c[i][pos]=1;
pos++;
}
}
}
void build(int p,int l,int r)//每一行进行操作
{
dat[p].reset(m+1,m+1);
if(l==r)
{
op(p,l);
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int x)
{
if(l==r)
{
op(p,l);
return;
}
int mid=(l+r)/2;
if(x<=mid)update(p<<1,l,mid,x);
else update(p<<1|1,mid+1,r,x);
pushup(p);
}
int main()
{
while(~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)
a[i][j]=(s[j]=='1');
}
build(1,1,n);
for(int i=1;i<=q;++i)
{
scanf("%d%d%d",&x,&y,&z);
if(x==1)
{
a[y][z]^=1;
update(1,1,n,y);//对第y行重求 m^3logn
}
else printf("%d\n",dat[1].c[y][z]);
}
}
return 0;
}
2N(1<=N<=14)个人,再给一个2N*2N的矩阵,第i行第j列的值为Vij(0<=Vij<=1e9)
让你把2N个人划分为大小均为N的两个团,使得同一团内成员i和成员j的权值Vij不计,
团1的任意成员i和团2的任意成员j的值Vij计一次(即,计Vij,就不计Vji)
求计入的答案的和的最大值
思路来源:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=40890350
考虑只统计i 答案max=矩阵右上角元素之和-团内答案min,所以最小化两个团内成员答案之和 暴搜,开始2*n个人,没有团,搜每个的时候,枚举这一个是丢给第一个团还是丢给第二个团 丢入时,去统计加入的点和团内已存在的点之间权值的贡献, 由于团的大小<=n,所以,复杂度 N*M(1<=N,M<=1e3,N*M>=2)的01矩阵,求全为1的次大子矩阵,大小是1的个数和来决定的 思路来源:https://blog.nowcoder.net/n/2f8fe0e0f24248358d9194d9a111f0df 抄一发人家题解,在处理最大子矩阵的时候,可以用单调栈 考虑每个竖条都是直方图上的一个竖条,那就变成了单调栈的裸题,看是否能向两端扩展,求最大子矩阵面积 那么预处理竖条,如果当前位置是1,当前位置的值就是上方的值+1 然后这里处理的是次大子矩阵,考虑3 2 3 2的样例,是由2 2 2的矩阵更新出来的 再例如0 1 1 0 是由1的矩阵更新出来的,单调栈扫每个子矩阵只会扫一次,且不会扫子矩阵内部的矩阵 所以更新x*y的矩阵的时候,需要一并更新(x-1)*y和x*(y-1)的内部部分,来保证次大值一定会得到更新 而悬线法每个点都扫,所以得判矩阵是不是同一个矩阵,下午抄板子WA了若干发才过, 悬线法可以地求子矩阵相关的问题,不会,待补 #include
H.Second Large Rectangle(次大01子矩阵/悬线法or单调栈)
#include