BJOI2018简要题解
R1T1二进制
发现一个二进制中的\(1\)对这个数模\(3\)的结果有\(1\)或\(2\)的贡献
所以当序列有偶数个\(1\)时,重排后就珂以
当序列里有奇数个\(1\)且个数不为\(1\),有至少\(2\)个\(0\)时,重排后就珂以
我们考虑用总数减去不合法的得出答案
首先,根据上面的结论,我们每次珂以线性得出答案,但是只有\(50pts\)
考虑动态dp,线段树维护
每个节点维护\(dl[0/1][0/1]\),表示左边一段连续区间中有\(0/1\)个\(0\),\(1\)个数的奇偶性为\(0/1\)的个数,类似的维护\(dr\)
还要维护\(fl[0/1/2]\),表示左边一段连续取件有\(0/1/2\)个\(0\),\(1\)个\(1\)的个数,类似的维护\(fr\)
转移显然,这样我们就得到了一个\(O((n+m)\log n)\)的做法
#include <bits/stdc++.h>
#define ll long long
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register ll x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct data{
ll dl[2][2],dr[2][2],fl[3],fr[3],s;
int l0,r0,s0,s1;
inline void init()
{
dl[0][0]=dl[0][1]=dl[1][0]=dl[1][1]=dr[0][0]=dr[0][1]=dr[1][0]=dr[1][1]=0;
fl[0]=fl[1]=fl[2]=fr[0]=fr[1]=fr[2]=0;
l0=r0=s0=s1=s=0;
}
inline void pre(register int x)
{
init();
if(x)
dl[0][1]=dr[0][1]=s1=s=fl[0]=fr[0]=1;
else
dl[1][0]=dr[1][0]=s0=l0=r0=1;
}
}tr[N<<2];
int n,a[N],m;
inline data pushup(register data A,register data B)
{
data c;
c.init();
for(register int i=0;i<=1;++i)
for(register int j=0;j<=1;++j)
{
c.dl[i][j]+=A.dl[i][j];
c.dr[i][j]+=B.dr[i][j];
if(i>=A.s0)
c.dl[i][j]+=B.dl[i-A.s0][j^(A.s1&1)];
if(i>=B.s0)
c.dr[i][j]+=A.dr[i-B.s0][j^(B.s1&1)];
}
for(register int i=0;i<=2;++i)
{
c.fl[i]+=A.fl[i];
c.fr[i]+=B.fr[i];
if(!A.s1)
c.fl[min(2,i+A.s0)]+=B.fl[i];
if(!B.s1)
c.fr[min(2,i+B.s0)]+=A.fr[i];
}
if(A.s1==1&&B.l0)
++c.fl[min(2,A.s0+B.l0)],c.fl[2]+=B.l0-1;
if(B.s1==1&&A.r0)
++c.fr[min(2,B.s0+A.r0)],c.fr[2]+=A.r0-1;
c.l0=(A.s1==0)?A.s0+B.l0:A.l0;
c.r0=(B.s1==0)?B.s0+A.r0:B.r0;
c.s0=A.s0+B.s0;
c.s1=A.s1+B.s1;
c.s=A.s+B.s;
c.s+=A.dr[0][0]*(B.dl[0][1]+B.dl[1][1]);
c.s+=A.dr[0][1]*(B.dl[0][0]+B.dl[1][0]);
c.s+=A.dr[1][0]*B.dl[0][1];
c.s+=A.dr[1][1]*B.dl[0][0];
if(B.l0)
c.s+=B.l0*(A.fr[0]+A.fr[1]+A.fr[2])-A.fr[0];
if(A.r0)
c.s+=A.r0*(B.fl[0]+B.fl[1]+B.fl[2])-B.fl[0];
return c;
}
inline void build(register int x,register int l,register int r)
{
if(l==r)
{
tr[x].pre(a[l]);
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
tr[x]=pushup(tr[x<<1],tr[x<<1|1]);
}
inline void modify(register int x,register int l,register int r,register int pos)
{
if(l==r)
{
tr[x].pre(a[l]);
return;
}
int mid=l+r>>1;
if(pos<=mid)
modify(x<<1,l,mid,pos);
else
modify(x<<1|1,mid+1,r,pos);
tr[x]=pushup(tr[x<<1],tr[x<<1|1]);
}
inline data query(register int x,register int l,register int r,register int L,register int R)
{
if(L<=l&&r<=R)
return tr[x];
int mid=l+r>>1;
if(R<=mid)
return query(x<<1,l,mid,L,R);
else if(L>mid)
return query(x<<1|1,mid+1,r,L,R);
else
return pushup(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
int main()
{
n=read();
for(register int i=1;i<=n;++i)
a[i]=read();
build(1,1,n);
m=read();
while(m--)
{
int opt=read();
if(opt==1)
{
int pos=read();
a[pos]^=1;
modify(1,1,n,pos);
}
else
{
int l=read(),r=read();
write(1ll*(r-l+1)*(r-l+2)/2-query(1,1,n,l,r).s),puts("");
}
}
return 0;
}
R1T2染色
神仙结论题
对于每个联通块分别考虑
首先,有奇环肯定不行
边数大于\(n+1\)肯定不行,至少有三个环
边数小于等于\(n\)的一定珂以
边数等于\(n+1\)的要拓扑排序特判一下
证明
#include <bits/stdc++.h>
#define N 20005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct node{
int to,nxt;
}e[N<<1];
int head[N>>1],cnt=0,du[N>>1];
inline void add(register int u,register int v)
{
e[++cnt]=(node){v,head[u]};
head[u]=cnt;
}
int T,n,m,col[N>>1],f,vtot,etot;
vector <int> pt;
queue <int> q;
inline void dfs(register int x,register int cl)
{
if(f)
return;
if(col[x]!=-1)
{
if(col[x]!=cl)
f=1;
return;
}
col[x]=cl,++vtot,pt.push_back(x);
for(register int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
++etot;
dfs(v,cl^1);
}
}
int main()
{
T=read();
while(T--)
{
cnt=f=0;
memset(head,0,sizeof(head));
memset(du,0,sizeof(du));
memset(col,-1,sizeof(col));
n=read(),m=read();
for(register int i=1;i<=m;++i)
{
int u=read(),v=read();
add(u,v),add(v,u);
++du[u],++du[v];
}
for(register int i=1;i<=n;++i)
if(col[i]==-1)
{
vtot=etot=0;
pt.clear();
dfs(i,0);
if(f)
break;
if(etot/2>vtot+1)
{
f=1;
break;
}
if(etot/2<=vtot)
continue;
for(register int j=0;j<pt.size();++j)
if(du[pt[j]]==1)
q.push(pt[j]);
while(!q.empty())
{
int u=q.front();
q.pop();
for(register int j=head[u];j;j=e[j].nxt)
{
int v=e[j].to;
if(--du[v]==1)
q.push(v);
}
}
int tot=0;
for(register int j=0;j<pt.size();++j)
if(du[pt[j]]==2)
{
int tmp=0;
for(register int k=head[pt[j]];k;k=e[k].nxt)
if(du[e[k].to]==3)
++tmp;
if(tmp==2)
++tot;
}
if(tot<2)
{
f=1;
break;
}
}
puts(f?"NO":"YES");
}
return 0;
}
R1T3求和
观察到\(k\)的值域很小是\([1,50]\),我们可以暴力预处理,并做个前缀和,查询树上差分即可,复杂度\(O(50n+m \log n)\)
#include <bits/stdc++.h>
#define mod 998244353
#define N 300005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct node{
int to,nxt;
}e[N<<1];
int head[N],cnt;
inline void add(register int u,register int v)
{
e[++cnt]=(node){v,head[u]};
head[u]=cnt;
}
int n,m,f[N][51],dep[N],siz[N],son[N],fa[N],top[N];
inline void dfs1(register int x,register int fat)
{
siz[x]=1;
for(register int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fat)
continue;
dep[v]=dep[x]+1;
for(register int j=1,t=dep[v];j<=50;++j,t=1ll*t*dep[v]%mod)
f[v][j]=(0ll+f[x][j]+t)%mod;
dfs1(v,x);
fa[v]=x;
siz[x]+=siz[v];
if(siz[v]>siz[son[x]])
son[x]=v;
}
}
inline void dfs2(register int x,register int t)
{
top[x]=t;
if(son[x])
dfs2(son[x],t);
for(register int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa[x]||v==son[x])
continue;
dfs2(v,v);
}
}
inline int getlca(register int x,register int y)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy])
swap(fx,fy),swap(x,y);
x=fa[fx];
fx=top[x];
}
return dep[x]<dep[y]?x:y;
}
int main()
{
n=read();
for(register int i=1;i<n;++i)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs1(1,0);
dfs2(1,1);
int m=read();
while(m--)
{
int u=read(),v=read(),k=read();
int lca=getlca(u,v);
write((0ll+f[u][k]+f[v][k]-f[lca][k]-f[fa[lca]][k]+2ll*mod)%mod),puts("");
}
return 0;
}
R2T1双人猜数游戏
神仙提答
设\(f_{x,y,k}\)表示\(x,y\)两个数,进行\(k\)轮,是否已经确定,暴力转移即可
#include <bits/stdc++.h>
#define N 300
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
int s,t;
char c;
int f[N+1][N+1][20];
inline int calc1(register int x,register int y,register int k)
{
int num=x*y,up=sqrt(x*y),xx=0,yy=0,cnt=0;
for(register int i=s;i<=up;++i)
if(num%i==0&&(!k||!f[i][num/i][k-1]))
xx=i,yy=num/i,++cnt;
return cnt==1&&xx==x&&yy==y;
}
inline int calc2(register int x,register int y,register int k)
{
int num=x+y,up=x+y>>1,xx=0,yy=0,cnt=0;
for(register int i=s;i<=up;++i)
if(!k||!f[i][num-i][k-1])
xx=i,yy=num-i,++cnt;
return cnt==1&&xx==x&&yy==y;
}
inline int calc3(register int x,register int y)
{
int num=x*y,up=sqrt(x*y),xx=0,yy=0,cnt=0;
for(register int i=s;i<=up;++i)
if(num%i==0&&f[i][num/i][t]&&(t<2||!f[i][num/i][t-2]))
xx=i,yy=num/i,++cnt;
return cnt==1&&xx==x&&yy==y;
}
inline int calc4(register int x,register int y)
{
int num=x+y,up=x+y>>1,xx=0,yy=0,cnt=0;
for(register int i=s;i<=up;++i)
if(f[i][num-i][t]&&(t<2||!f[i][num-i][t-2]))
xx=i,yy=num-i,++cnt;
return cnt==1&&xx==x&&yy==y;
}
inline void Work(register int x,register int y)
{
if(!f[x][y][t])
return;
for(register int k=0;k<t;++k)
if(f[x][y][k])
return;
int nw=((t&1)&&c=='A')||(!(t&1)&&c=='B');
int fl=nw?calc3(x,y):calc4(x,y);
if(fl)
write(x),putchar(' '),write(y),exit(0);
}
int main()
{
s=read();
c=getchar();
while(c!='A'&&c!='B')
c=getchar();
t=read();
for(register int k=0,nw=c=='A';k<=t;++k,nw^=1)
for(register int i=s;i<=N;++i)
for(register int j=i;j<=N;++j)
{
if(k>1)
f[i][j][k]=f[i][j][k-2];
f[i][j][k]|=nw?calc1(i,j,k):calc2(i,j,k);
}
for(register int sum=s<<1;;++sum)
for(register int i=s;i<=sum>>1;++i)
Work(i,sum-i);
return 0;
}
R2T2链上二次求和
因为是一条链,所以我们可以放到区间上去看(注意:因为是链,所以修改/查询的区间有可能会出现\(l>r\)的情况)
设\(S\)为数列的前缀和,\(SS\)为\(S\)的前缀和
\[\sum_{i=l}^{r} \sum_{j=i}^{n}\left(S_{j}-S_{j-i}\right)=\sum_{i=l}^{r}\left(\sum_{j=i}^{n} S_{j}-\sum_{j=0}^{n-i} S_{j}\right)=\sum_{i=l}^{r}\left(S S_{n}-S S_{i-1}-S S_{n-i}\right)
\]
这个珂以用线段树维护
对于每个修改操作,我们要考虑对SS每个位置的贡献
-
\(l \leq i \leq r , \frac{(i-l+1)(i-l+2)v}{2}\)
-
\(r<i , (\frac{(r-l+1)(r-l+2)}{2}+(i-r)(r-l+1))v\)
线段树上维护二次函数区间加即可,复杂度为\(O((n+m)\log n)\)
#include <bits/stdc++.h>
#define mod 1000000007
#define N 200005
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline void add(register int &x,register int y)
{
x+=y;
if(x>=mod)
x-=mod;
}
const int inv2=500000004,inv6=166666668;
int n,m;
int a[N<<3],b[N<<3],c[N<<3],t[N<<3];
inline int S(register int n)
{
return 1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
}
inline void put(register int x,register int l,register int r,register int A,register int B,register int C)
{
int s0=r-l+1,s1=1ll*(l+r)*(r-l+1)/2%mod;
int s2=(S(r)-S(l-1)+mod)%mod;
add(t[x],1ll*A*s2%mod);
add(t[x],1ll*B*s1%mod);
add(t[x],1ll*C*s0%mod);
add(a[x],A),add(b[x],B),add(c[x],C);
}
inline void pushdown(register int x,register int l,register int r)
{
if(!(a[x]||b[x]||c[x]))
return;
int mid=l+r>>1;
put(x<<1,l,mid,a[x],b[x],c[x]);
put(x<<1|1,mid+1,r,a[x],b[x],c[x]);
a[x]=b[x]=c[x]=0;
}
inline void modify(register int x,register int l,register int r,register int insl,register int insr,register int a,register int b,register int c)
{
if(insl<=l&&r<=insr)
{
put(x,l,r,a,b,c);
return;
}
int mid=l+r>>1;
pushdown(x,l,r);
if(insl<=mid)
modify(x<<1,l,mid,insl,insr,a,b,c);
if(insr>mid)
modify(x<<1|1,mid+1,r,insl,insr,a,b,c);
t[x]=(t[x<<1]+t[x<<1|1])%mod;
}
inline int query(register int x,register int l,register int r,register int insl,register int insr)
{
if(insl<=l&&r<=insr)
return t[x];
pushdown(x,l,r);
int mid=l+r>>1,res=0;
if(insl<=mid)
res+=query(x<<1,l,mid,insl,insr);
if(insr>mid)
res+=query(x<<1|1,mid+1,r,insl,insr);
return res%mod;
}
int main()
{
n=read(),m=read();
for(register int i=1,x,s=0,ss=0;i<=n;++i)
{
x=read();
add(s,x);
add(ss,s);
modify(1,0,n,i,i,0,0,ss);
}
for(register int i=1;i<=m;++i)
{
int op=read();
if(op==1)
{
int l=read(),r=read(),v=read();
if(l>r)
l^=r^=l^=r;
v=1ll*v*inv2%mod;
int a=v,b=(1ll*(3-2*l)*v%mod+mod)%mod;
int c=(1ll*v*(1ll*l*l%mod-3ll*l+2)%mod+mod)%mod;
modify(1,0,n,l,r,a,b,c);
a=0,b=2ll*(r-l+1)*v%mod;
c=(1ll*(r-l+1)*(r-l+2)%mod*v%mod-1ll*r*b%mod+mod)%mod;
if(r!=n)
modify(1,0,n,r+1,n,a,b,c);
}
else
{
int l=read(),r=read();
if(l>r)
l^=r^=l^=r;
int ans=1ll*query(1,0,n,n,n)*(r-l+1)%mod;
add(ans,mod-query(1,0,n,l-1,r-1));
add(ans,mod-query(1,0,n,n-r,n-l));
write(ans),puts("");
}
}
return 0;
}
R2T3治疗之雨
题意好像不太清楚,或许是我语文太差
实际就是每轮操作随机一个不满血的加一滴血,随机\(k\)个减一滴血,问期望多少回合后血量会为0
我们设\(p_x\)表示一次操作扣\(x\)滴血的概率
\[p_x=\tbinom{k}{x} (\frac{1}{m+1})^x (\frac{m}{m+1})^{k-x}
\]
我们设\(E_x\)表示还有\(i\)滴血时期望多少回合后血量会为0
对于\(i \in [1,n-1]\)
\[E_{i}=\left(\sum_{j=1}^{j}\left(\frac{m}{m+1} p_{i-j}+\frac{1}{m+1} p_{i-j+1}\right) E_{j}\right)+\left(\frac{1}{m+1} p_{0}\right) E_{i+1}+1
\]
对于\(i=n\)
\[E_{n}=\left(\sum_{j=1}^{n} p_{n-j} E_{j}\right)+1
\]
进行高斯消元即可,于是我们就得到了能拿70pts的好做法
发现这个矩阵很有特点,几乎是一个下三角矩阵,所以我们珂以用\(n^2\)的时间求解,总复杂度\(O(Tn^2)\)
#include <bits/stdc++.h>
#define mod 1000000007
#define N 1505
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline int power(register int a,register int b)
{
int res=1;
while(b)
{
if(b&1)
res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
int T,n,p,m,k,inv[N],P[N],F[N][N],f[N];
inline int work()
{
memset(P,0,sizeof(P));
n=read(),p=read(),m=read(),k=read();
if(!k||(m==0&&k==1))
return -1;
if(m==0)
{
int res=0;
for(;p>0;p-=k,++res)
if(p<n)
++p;
return res;
}
int inva=power(m+1,mod-2);
inv[1]=1;
for(register int i=2;i<=n+1;++i)
inv[i]=mod-1ll*mod/i*inv[mod%i]%mod;
for(register int i=0,C=1;i<=min(n,k);C=1ll*C*inv[i+1]%mod*(k-i)%mod,++i)
P[i]=1ll*C*power(inva,i)%mod*power(1ll*m*inva%mod,k-i)%mod;
for(register int i=1;i<n;++i)
{
for(register int j=1;j<=i;++j)
F[i][j]=(1ll*m*inva%mod*P[i-j]%mod+1ll*inva*P[i-j+1]%mod)%mod;
F[i][i+1]=1ll*inva*P[0]%mod;
(F[i][i]+=mod-1)%=mod;
F[i][n+1]=mod-1;
}
for(register int i=1;i<=n;++i)
F[n][i]=P[n-i];
(F[n][n]+=mod-1)%=mod;
F[n][n+1]=mod-1;
for(register int i=n;i>=2;--i)
{
if(!F[i][i])
return -1;
int t=1ll*F[i-1][i]*power(F[i][i],mod-2)%mod;
for(register int j=i;j>=1;--j)
F[i-1][j]=(F[i-1][j]-1ll*F[i][j]*t%mod+mod)%mod;
F[i-1][n+1]=(F[i-1][n+1]-1ll*F[i][n+1]*t%mod+mod)%mod;
}
for(register int i=1;i<=n;++i)
{
int res=F[i][n+1];
for(register int j=1;j<i;++j)
res=(res-1ll*f[j]*F[i][j]%mod+mod)%mod;
f[i]=1ll*res*power(F[i][i],mod-2)%mod;
}
return f[p];
}
int main()
{
T=read();
while(T--)
write(work()),puts("");
return 0;
}