二进制
序列上线段树维护DDP好题。
题解可以看这篇
代码:
#include
#define ri register int
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n;
bool a[N];
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
inline int add(const ll&a,const ll&b){return !a*b?0:a+b;}
namespace SGT{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
struct Node{int l,r,det;ll ans,c[2][2][2][2],d[2][2];}T[N<<2];
inline Node operator+(const Node&a,const Node&b){
Node ret;
ret.l=a.l,ret.r=b.r,ret.det=a.det+b.det,ret.ans=a.ans+b.ans,memset(ret.c,0,sizeof(ret.c));
ret.d[0][0]=a.d[0][0]+(a.det?0:b.d[0][0]);
ret.d[0][1]=a.d[0][1]+(a.det<2?b.d[0][1-a.det]:0);
ret.d[1][0]=b.d[1][0]+(b.det?0:a.d[1][0]);
ret.d[1][1]=b.d[1][1]+(b.det<2?a.d[1][1-b.det]:0);
for(ri i=0;i<2;++i)for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)ret.c[0][i][j][k]=a.c[0][i][j][k],ret.c[1][i][j][k]=b.c[1][i][j][k];
int tl=a.r-a.l+1-a.det,tr=b.r-b.l+1-b.det;
for(ri k=0;k+tl<2;++k){
ret.c[0][0][0][k+tl]+=a.det?0:b.c[0][0][0][k];
ret.c[0][0][1][k+tl]+=b.c[0][a.det&1][1][k]+(a.det?b.c[0][a.det&1][0][k]:0);
ret.c[0][1][0][k+tl]+=a.det<2?b.c[0][1-a.det][0][k]:0;
ret.c[0][1][1][k+tl]+=b.c[0][(a.det&1)^1][1][k]+(a.det>1?b.c[0][(a.det&1)^1][0][k]:0);
}
for(ri k=0;k+tr<2;++k){
ret.c[1][0][0][k+tr]+=b.det?0:a.c[1][0][0][k];
ret.c[1][0][1][k+tr]+=a.c[1][b.det&1][1][k]+(b.det?a.c[1][b.det&1][0][k]:0);
ret.c[1][1][0][k+tr]+=b.det<2?a.c[1][1-b.det][0][k]:0;
ret.c[1][1][1][k+tr]+=a.c[1][(b.det&1)^1][1][k]+(b.det>1?a.c[1][(b.det&1)^1][0][k]:0);
}
for(ri i=0;i<2;++i)for(ri j=0;i+j<2;++j){
ret.ans+=a.c[1][0][0][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][1][j]+a.c[1][0][1][i]*b.c[0][1][0][j];
ret.ans+=a.c[1][1][0][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][1][j]+a.c[1][1][1][i]*b.c[0][0][0][j];
}
ret.ans+=a.d[1][0]*b.d[0][1]+a.d[1][1]*b.d[0][0];
return ret;
}
inline void solve(int p){
T[p].ans=T[p].det=0,memset(T[p].c,0,sizeof(T[p].c)),memset(T[p].d,0,sizeof(T[p].d));
if(a[T[p].l])T[p].ans=T[p].det=T[p].c[0][1][0][0]=T[p].c[1][1][0][0]=T[p].d[0][1]=T[p].d[1][1]=1;
else T[p].c[0][0][0][1]=T[p].c[1][0][0][1]=T[p].d[0][0]=T[p].d[1][0]=1;
}
inline void build(int p,int l,int r){
T[p].l=l,T[p].r=r;
if(l==r)return solve(p);
build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
}
inline void update(int p,int k){
if(T[p].l==T[p].r)return solve(p);
update(k<=mid?lc:rc,k),T[p]=T[lc]+T[rc];
}
inline Node query(int p,int ql,int qr){
if(ql<=T[p].l&&T[p].r<=qr)return T[p];
if(qr<=mid)return query(lc,ql,qr);
if(ql>mid)return query(rc,ql,qr);
return query(lc,ql,mid)+query(rc,mid+1,qr);
}
}
int main(){
n=read();
for(ri i=1;i<=n;++i)a[i]=read();
SGT::build(1,1,n);
for(ri tt=read(),op,l,r;tt;--tt){
op=read();
if(op==1)a[l=read()]^=1,SGT::update(1,l);
else l=read(),r=read(),cout<<(ll)(r-l+1)*(r-l+2)/2-SGT::query(1,l,r).ans<<'\n';
}
return 0;
}
染色
结论神题。
首先奇环先判掉(这样已经能过非hack数据了
然后对于偶环的情况分类讨论。
对于一个连通块来说。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
const int N=10005;
vector<int>e[N],a[N];
int n,m,dfn[N],stk[N],low[N],du[N],sig=0,tot=0,top=0,res,ec,pc,ck=0;
bool vis[N],in[N],col[N];
bool dfs(int p,bool f){
vis[p]=1,col[p]=f;
for(ri i=0,v;i<e[p].size();++i){
if(vis[v=e[p][i]]){if(col[p]^col[v])continue;return 0;}
if(!dfs(v,f^1))return 0;
}
return 1;
}
void tarjan(int p){
++pc,stk[++top]=p,dfn[p]=low[p]=++tot;
for(ri i=0,v;i<e[p].size();++i){
++ec;
if(dfn[v=e[p][i]])low[p]=min(dfn[v],low[p]);
else{
tarjan(v);
if(low[v]>=dfn[p]){
a[++sig].clear();
a[sig].push_back(p);
int x;
do a[sig].push_back((x=stk[top--]));while(x^v);
ck+=a[sig].size()>=4;
}
low[p]=min(low[p],low[v]);
}
}
}
void solve(int p,int fa,int ed,int len){
if(p==ed){res+=len==2;return;}
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa&&in[v])solve(v,p,ed,len+1);
}
inline void Solve(){
for(ri i=1;i<=n;++i)if(!vis[i])if(!dfs(i,0)){puts("NO");return;}
for(ri tt=1;tt<=n;++tt){
if(dfn[tt])continue;
ec=pc=tot=top=sig=ck=0;
tarjan(tt);
if(ck>2){puts("NO");return;}
ec>>=1;
if(ec<=pc)continue;
if(ec>=pc+2){puts("NO");return;}
int st=-1,ed=-1,pos=0;
while(a[++pos].size()<4);
for(ri i=0;i<a[pos].size();++i)in[a[pos][i]]=1,du[a[pos][i]]=0;
for(ri i=0,u;i<a[pos].size();++i){
u=a[pos][i];
for(ri j=0;j<e[u].size();++j)if(in[e[u][j]])++du[u];
if(du[u]==3)~st?ed=u:st=u;
}
res=0,solve(st,0,ed,0);
if(res>=2)continue;
puts("NO");
return;
}
puts("YES");
}
int main(){
for(ri tt=read();tt;--tt){
n=read(),m=read();
for(ri i=1;i<=n;++i)e[i].clear(),dfn[i]=vis[i]=in[i]=low[i]=0;
for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
Solve();
}
return 0;
}
求和
貌似是个让处理树上前缀和的送分题?
代码:
#include
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int N=3e5+5,mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
int n,dep[N],top[N],fa[N],hson[N],siz[N],dis[N][51];
vector<int>e[N];
void dfs1(int p){
siz[p]=1;
dis[p][0]=1;
for(ri i=1;i<=50;++i)dis[p][i]=mul(dis[p][i-1],dep[p]);
for(ri i=0;i<=50;++i)dis[p][i]=add(dis[p][i],dis[fa[p]][i]);
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
}
}
void dfs2(int p,int tp){
top[p]=tp;
if(!hson[p])return;
dfs2(hson[p],tp);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa[p]&&v!=hson[p])dfs2(v,v);
}
inline int lca(int x,int y){
while(top[x]^top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline void query(int x,int y,int k){
int t=lca(x,y);
cout<<dec(add(dis[x][k],dis[y][k]),add(dis[t][k],dis[fa[t]][k]))<<'\n';
}
int main(){
n=read();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
dfs1(1),dfs2(1,1);
for(ri tt=read(),x,y;tt;--tt)x=read(),y=read(),query(x,y,read());
exit(0);
}
双人猜数游戏
这个题并没有链接 b z o j bzoj bzoj不支持提答。
然后这个题我们用 d p dp dp。
f i , j , t f_{i,j,t} fi,j,t表示一个数为 i i i另一个为 j j j时 t t t轮后能否都猜出来。
转移分几种情况讨论一下。
用来打表的代码借鉴了 z x y y y _ a k _ u u u o i zxyyy\_ak\_uuuoi zxyyy_ak_uuuoi的。
代码:
#include
#define ri register int
using namespace std;
typedef long long ll;
const int N=505;
int s,t;
bool f[N][N][20],flag,ff;
string name;
inline bool check1(int x,int y,int t){
int res=0;
for(ri i=s,X=x*y,up=sqrt(X),cnt=0;i<=up;++i)if(X==X/i*i)if(!t||!f[i][X/i][t-1]){++cnt,res=i;if(cnt>1)return 0;}
return res==x;
}
inline bool check2(int x,int y,int t){
int res=0;
for(ri i=s,X=x+y,up=X>>1,cnt=0;i<=up;++i)if(!t||!f[i][X-i][t-1]){++cnt,res=i;if(cnt>1)return 0;}
return res==x;
}
inline bool Check1(int x,int y){
int res=0;
for(ri i=s,X=x*y,up=sqrt(X),cnt=0;i<=up;++i)if(X==X/i*i)if((t<2||!f[i][X/i][t-2])&&f[i][X/i][t]){++cnt,res=i;if(cnt>1)return 0;}
return res==x;
}
inline bool Check2(int x,int y){
int res=0;
for(ri i=s,X=x+y,up=X>>1,cnt=0;i<=up;++i)if((t<2||!f[i][X-i][t-2])&&f[i][X-i][t]){++cnt,res=i;if(cnt>1)return 0;}
return res==x;
}
int main(){
cin>>s>>name>>t,ff=flag=name=="Alice";
for(ri tt=0;tt<=t;++tt,flag^=1)for(ri i=s;i<=500;++i)for(ri j=s;j<=500;++j){
if(tt>=2)f[i][j][tt]=f[i][j][tt-2];
if(!f[i][j][tt])f[i][j][tt]=flag?check1(i,j,tt):check2(i,j,tt);
}
for(ri sum=s<<1;;++sum){
for(ri i=s,j;i<=sum>>1;++i){
j=sum-i;
if(!f[i][j][t])continue;
flag=1;
for(ri k=0;k<t;++k)if(f[i][j][k]){flag=0;break;}
if(!flag)continue;
if(((t&1)==ff?Check1(i,j):Check2(i,j)))return cout<<i<<' '<<j<<'\n',0;
}
}
return 0;
}
链上二次求和
线段树菜题。
题解见这里。
代码:
#include
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const ll mod=1e9+7,N=2e5+5,inv=5e8+4;
int n,m;
ll a[N],s1[N],s2[N];
struct Node{ll l1,l2,l3,sum,a,b,c;}T[N<<2];
inline void pushup(int p){T[p].sum=(T[lc].sum+T[rc].sum)%mod;}
inline void pushnow(int p,ll a,ll b,ll c){
T[p].a+=a,T[p].b+=b,T[p].c+=c,(T[p].sum+=a*T[p].l1+b*T[p].l2+c*T[p].l3)%=mod;
if(T[p].a>=mod)T[p].a-=mod;
if(T[p].b>=mod)T[p].b-=mod;
if(T[p].c>=mod)T[p].c-=mod;
}
inline void pushdown(int p){
if(!T[p].a&&!T[p].b&&!T[p].c)return;
pushnow(lc,T[p].a,T[p].b,T[p].c),pushnow(rc,T[p].a,T[p].b,T[p].c);
T[p].a=T[p].b=T[p].c=0;
}
inline void build(int p,int l,int r){
T[p].l3=r-l+1,T[p].l2=s1[r]-(l?s1[l-1]:0),T[p].l1=s2[r]-(l?s2[l-1]:0);
if(l==r){T[p].sum=a[l];return;}
build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int l,int r,int ql,int qr,ll a,ll b,ll c){
if(ql>r||qr<l)return;
if(ql<=l&&r<=qr)return pushnow(p,a,b,c);
pushdown(p);
if(qr<=mid)update(lc,l,mid,ql,qr,a,b,c);
else if(ql>mid)update(rc,mid+1,r,ql,qr,a,b,c);
else update(lc,l,mid,ql,mid,a,b,c),update(rc,mid+1,r,mid+1,qr,a,b,c);
pushup(p);
}
inline ll query(int p,int l,int r,int ql,int qr){
if(ql>r||qr<l)return 0;
if(ql<=l&&r<=qr)return T[p].sum;
pushdown(p);
if(qr<=mid)return query(lc,l,mid,ql,qr);
if(ql>mid)return query(rc,mid+1,r,ql,qr);
return (query(lc,l,mid,ql,mid)+query(rc,mid+1,r,mid+1,qr))%mod;
}
int main(){
n=read(),m=read();
for(ri i=1;i<=n;++i)a[i]=(read()+a[i-1])%mod,s1[i]=(s1[i-1]+i)%mod,s2[i]=(s2[i-1]+(ll)i*i%mod)%mod;
for(ri i=1;i<=n;++i)(a[i]+=a[i-1])%=mod;
build(1,1,n);
while(m--){
int op=read(),l=read(),r=read(),len;
ll v;
if(l>r)swap(l,r);
if(op==1){
v=read(),len=r-l+1;
ll a=v*inv%mod,b=v*(3-2*l+mod)%mod*inv%mod,c=v*(l-1)%mod*(l-2)%mod*inv%mod;
update(1,1,n,l,r,a,b,c);
if(r==n)continue;
a=0,b=v*len%mod,c=(((ll)(len+1)*len/2%mod*v-v*len%mod*r)%mod+mod)%mod;
update(1,1,n,r+1,n,a,b,c);
}
else{
l=max(l,1);
ll s1=query(1,1,n,n,n)*(r-l+1)%mod,s2=query(1,1,n,max(l-1,1),r-1),s3=query(1,1,n,max(n-r,1),n-l);
cout<<(s1-s2-s3+mod*3)%mod<<'\n';
}
}
}
治疗之雨
期望 d p dp dp中档题。
列出式子之后移项。
然后会发现这个东西是一个方程。
我们令 f i f_i fi表示还剩 i i i滴血时死掉的期望轮数,且 f 1 = x f_1=x f1=x,那么 f 2 , f 3 , . . . , f n f_2,f_3,...,f_n f2,f3,...,fn均可用 A x + B Ax+B Ax+B表示出来,然后最后把 x x x解出来就完了。
注意预处理系数时候的次数上界。
代码:
#include
#define ri register int
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
typedef long long ll;
const int mod=1e9+7,N=1505;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
int n,P,k,m,p[N];
struct coe{
int x,y;
coe(int _x=0,int _y=0):x(_x),y(_y){}
friend inline coe operator+(const coe&a,const coe&b){return coe(add(a.x,b.x),add(a.y,b.y));}
friend inline coe operator-(const coe&a,const coe&b){return coe(dec(a.x,b.x),dec(a.y,b.y));}
friend inline coe operator*(const coe&a,const int&b){return coe(mul(a.x,b),mul(a.y,b));}
}f[N];
inline void solve(){
p[0]=ksm(mul(m,ksm(m+1,mod-2)),k);
if(p[0]==1||!p[0]){puts("-1");return;}
for(ri i=1,invm=ksm(m,mod-2),up=min(k,n);i<=up;++i)p[i]=mul(mul(p[i-1],k-i+1),mul(ksm(i,mod-2),invm));
int inv=ksm(p[0],mod-2),x0;
f[0]=coe(0,0),f[1]=coe(1,0);
for(ri i=1;i<n;++i){
f[i+1]=(f[i]-coe(0,1))*(m+1);
for(ri j=1,up=min(i+1,k);j<=up;++j)f[i+1]=f[i+1]-(f[i+1-j]*p[j]);
for(ri j=0,up=min(i,k);j<=up;++j)f[i+1]=f[i+1]-(f[i-j]*mul(p[j],m));
f[i+1]=f[i+1]*inv;
}
coe tmp=coe(0,1);
for(ri i=1,up=min(n,k);i<=up;++i)tmp=tmp+(f[n-i]*p[i]);
tmp=(tmp*ksm(dec(1,p[0]),mod-2))-f[n];
if(!tmp.x&&f[P].x){puts("-1");return;}
x0=tmp.x?mul(mod-tmp.y,ksm(tmp.x,mod-2)):0;
cout<<add(mul(f[P].x,x0),f[P].y)<<'\n';
}
int main(){
for(ri tt=read();tt;--tt){
n=read(),P=read(),m=read(),k=read();
if(!k){puts("-1");continue;}
if(!m){
if(k==1&&n!=1)puts("-1");
else{
p[0]=0;
for(ri i=1;i<n;++i)p[i]=p[max(i+1-k,0)]+1;
p[n]=p[max(n-k,0)]+1;
cout<<p[P]<<'\n';
}
continue;
}
solve();
}
return 0;
}