Preface
这场的B和C都挺不错的,最后花了2.5h Rush B还好搞出来了
但是A被SB题面和自己坑了,直接GG
由于没有题解因此C的做法是陈指导的,但是要讨论好多细节就口胡了
序列
显然把输入的数看做点然后前面的向后面连有向边,每次能取的点就是入度为(0)的点
因此做一个用堆维护最小标号的拓扑排序即可,复杂度(O(nlog n))
#include<cstdio>
#include<queue>
#include<cctype>
#include<iostream>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=1000005;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
struct edge
{
int to,nxt;
}e[N]; int n,k,x,head[N],cnt,deg[N],lst,mx; bool vis[N];
priority_queue < int,vector<int>,greater<int> > q;
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[y];
}
int main()
{
freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
RI i,j; for (F.read(n),i=1;i<=n;++i)
for (F.read(k),j=1,lst=0;j<=k;lst=x,++j)
F.read(x),vis[x]=1,lst&&(addedge(lst,x),0),mx=max(mx,x);
for (i=1;i<=mx;++i) if (vis[i]&&!deg[i]) q.push(i);
while (!q.empty())
{
int now=q.top(); q.pop(); printf("%d ",now);
for (i=head[now];i;i=e[i].nxt)
if (!(--deg[e[i].to])) q.push(e[i].to);
}
return 0;
}
试剂
计数题要么考虑DP要么考虑拆贡献,要么两个一起用
我们考虑乘法很难通过计算方案数来求贡献,因此我们考虑如何避免
枚举(i),表示先将(i)个数做乘法操作,然后再与其他数进行加法,此时我们如果算出方案数显然就是它的贡献
我们发现此时数被分成了两类,一类是我们强制选的只进行乘法的数(叫做(A)类),另一类是可以随便选的数(叫做(B))
我们可以设(f_{i,j})表示(i)个(A)类数和(j)个(B)类数的操作方案数,转移很简单但是也有挺多细节
然后预处理出(g_i)表示有(i)个数相乘的所有方案数的和,因此答案就是(sum_{i=1}^n g_i imes f_{i,n-i})
DP的转移细节看代码,复杂度(O(n^2))
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=3005;
int n,mod,f[N][N],g[N],x,ans;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
freopen("reagent.in","r",stdin); freopen("reagent.out","w",stdout);
RI i,j; for (scanf("%d%d",&n,&mod),g[0]=i=1;i<=n;++i)
for (scanf("%d",&x),j=i;j;--j) inc(g[j],1LL*g[j-1]*x%mod);
for (f[1][0]=i=1;i<=n;++i) for (j=0;j<=n;++j)
{
if (j) inc(f[i][j],1LL*i*j%mod*f[i][j-1]%mod);
if (j>1) inc(f[i][j],1LL*j*(j-1)%mod*f[i][j-1]%mod);
if (i>1) inc(f[i][j],1LL*i*(i-1)/2LL%mod*f[i-1][j]%mod);
}
for (i=1;i<=n;++i) inc(ans,1LL*g[i]*f[i][n-i]%mod);
return printf("%d",ans),0;
}
立体计算几何
首先先求出能覆盖所有点的最小长方体,由此我们可以很容易地判断是否合法
考虑接下来就是判断一个询问是CLOSED
还是UNKNOWN
了,我们考虑对于已知的最小长方体进行扩展,使得要询问的点恰好在其边界上,然后询问新的长方体中是否有不在立方体内的点即可
因此问题本质就是个三维数点,两个$log $应该是过不去的,但是由于有特殊性质可以优化
考虑我们给(x)维和询问离线,以(y)维的值为下标,(z)维的值为存储值建一棵线段树,因此现在的问题就是要求在给定(x,y)维的信息后,一个区间([z_1,z_2])中是否存在不在长方体内的点
可以通过在线段树上维护最小的(>z_1)的值以及最大的(<z_2)的值来实现询问,复杂度就是(O(nlog n))的了
以下是陈指导的代码(细节真多不想写)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define K 1000000
using namespace std;
int n,m,k,xm,ym,zm,x1,x2,Y1,y2,z1,z2,ans[N+5];
struct P
{
int x,y,z;I P(CI a=0,CI b=0,CI c=0):x(a),y(b),z(c){}
I bool operator < (Con P& o) Con {return x^o.x?x<o.x:(y^o.y?y<o.y:z<o.z);}
}p[N+5];
struct Q
{
int p,x,y,z;I Q(CI id=0,CI a=0,CI b=0,CI c=0):p(id),x(a),y(b),z(c){}
I bool operator < (Con Q& o) Con {return x^o.x?x<o.x:(y^o.y?y<o.y:z<o.z);}
}q[K+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
#undef D
}F;
map<P,int> V;I void asdf()
{
RI i,x,y,z;for(puts("CORRECT"),i=1;i<=m;++i) V[p[i]]=1;
for(i=1;i<=k;++i) F.read(x),F.read(y),F.read(z),puts(V[P(x,y,z)]?"CLOSED":"UNKNOWN");
}
I void Check()
{
RI i;for(i=1;i<=m;++i) if(x1<=p[i].x&&p[i].x<=x2&&
Y1<=p[i].y&&p[i].y<=y2&&z1<=p[i].z&&p[i].z<=z2) puts("INCORRECT"),exit(0);
puts("CORRECT");
}
int y3,y4;
class SegmentTree
{
private:
#define PT CI l=1,CI r=ym,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (Mn[x]=min(Mn[x<<1],Mn[x<<1|1]),Mx[x]=max(Mx[x<<1],Mx[x<<1|1]))
int Mn[N<<2],Mx[N<<2];
public:
I void Init(PT) {if(Mn[rt]=zm+1,Mx[rt]=0,l==r) return;RI mid=l+r>>1;Init(LT),Init(RT);}
I void U(CI y,CI z,PT)
{
if(l==r) return (void)(z>=z1&&(Mn[rt]=min(Mn[rt],z)),z<=z2&&(Mx[rt]=max(Mx[rt],z)));
RI mid=l+r>>1;y<=mid?U(y,z,LT):U(y,z,RT),PU(rt);
}
I bool Q(CI L,CI R,CI U,CI D,PT)
{
if(L<=l&&r<=R) return (Mn[rt]>D&&Mx[rt]<U);RI mid=l+r>>1;
return (L<=mid?Q(L,R,U,D,LT):1)&&(R>mid?Q(L,R,U,D,RT):1);
}
}S;
I int Qry(CI y,CI z) {return S.Q(min(Y1,y),max(y2,y),min(z1,z),max(z2,z))-1;}
int main()
{
freopen("zyqh.in","r",stdin),freopen("zyqh.out","w",stdout);
RI i,j,t,x,y,z;F.read(xm),F.read(ym),F.read(zm),F.read(n),F.read(m),F.read(k),x1=xm,Y1=ym,z1=zm;
for(i=1;i<=n;++i) F.read(x),F.read(y),F.read(z),
x1=min(x1,x),x2=max(x2,x),Y1=min(Y1,y),y2=max(y2,y),z1=min(z1,z),z2=max(z2,z);
for(i=1;i<=m;++i) F.read(p[i].x),F.read(p[i].y),F.read(p[i].z);sort(p+1,p+m+1);
if(!n) return asdf(),0;Check();
for(i=1;i<=k;++i) F.read(q[i].x),F.read(q[i].y),F.read(q[i].z),q[i].p=i;sort(q+1,q+k+1);
RI l=0,r=m+1;W(l^m&&p[l+1].x<x1) ++l;W(r^1&&p[r-1].x>x2) --r;
RI a=0,b=k+1;W(a^k&&q[a+1].x<x1) ++a;W(b^1&&q[b-1].x>x2) --b;
#define C(i) (x1<=q[i].x&&q[i].x<=x2&&Y1<=q[i].y&&q[i].y<=y2&&z1<=q[i].z&&q[i].z<=z2&&(ans[q[i].p]=1))
for(S.Init(),i=l+1;i<=r-1;++i) S.U(p[i].y,p[i].z);
for(i=a+1;i<=b-1;++i) !C(i)&&(ans[q[i].p]=Qry(q[i].y,q[i].z));
for(S.Init(),i=l+1;i<=r-1;++i) S.U(p[i].y,p[i].z);for(i=a,t=l;i>=1;--i)
{W(t>=1&&p[t].x>=q[i].x) S.U(p[t].y,p[t].z),--t;!C(i)&&(ans[q[i].p]=Qry(q[i].y,q[i].z));}
for(S.Init(),i=l+1;i<=r-1;++i) S.U(p[i].y,p[i].z);for(i=b,t=r;i<=k;++i)
{W(t<=m&&p[t].x<=q[i].x) S.U(p[t].y,p[t].z),++t;!C(i)&&(ans[q[i].p]=Qry(q[i].y,q[i].z));}
for(i=1;i<=k;++i) puts(ans[i]?(~ans[i]?"OPEN":"CLOSED"):"UNKNOWN");return 0;
}
Postscript
陈指导是我们的红太阳!!!