环
将 \(\{a\}\) 排序
考虑计算强制所有点都在环上的连边方案数,设 \(f_{i,j,k}\) 表示剩下 \(j\) 个单点和 \(k\) 条链时的方案数,转移讨论 合并两个单点/合并单点及链/合并两条链 三种情况即可
由于链可以双向使用,那么每次添加新链视作两个,而合并两个链的系数为 \(k(k-2)\)
其实这里就能发现并没有必要将链单独表示成一维,根据实际含义视作链尾的点即可压成两维
那么不强制所有点在环上的方案数就是在进行 \(a_i-a_{i-1}\) 个点的添加时枚举添加点数 \(d\) 进行对应转移到 \(f_{i,j+d}\) 同时要附加 \(\binom{a_i-a_{i-1}}d\) 的系数即可
如果 \(j+d=1\) 时再产生合并就得到了一个大环,此时进行更新答案
注意这种计算方式将二元环计入了答案,需要减去;同时每个环被正反计算了两次,要除 \(2\)
Code Display
const int N=5010;
int dp[2][N],fac[N],ifac[N],n,a[N],ans;
inline int C(int n,int m){return mul(fac[n],mul(ifac[m],ifac[n-m]));}
signed main(){
freopen("ring.in","r",stdin); freopen("ring.out","w",stdout);
n=read(); fac[0]=1;
rep(i,1,n) a[i]=read(),fac[i]=mul(fac[i-1],i);
ifac[n]=ksm(fac[n],mod-2);
Down(i,n,1) ifac[i-1]=mul(ifac[i],i);
sort(a+1,a+n+1);
int cur=0;
dp[cur][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=n;++j) if(dp[cur][j]){
dp[cur][j]%=mod;
int num=a[i]-a[i-1];
for(int k=0;k<=num;++k){
int coef=C(num,k);
dp[cur^1][j+k]+=mul(coef,dp[cur][j]);
if(j+k>=2) dp[cur^1][j+k-1]+=(j+k)*(j+k-1)*mul(coef,dp[cur][j])%mod;
else if(j+k==1) ckadd(ans,mul(dp[cur][j],coef));
}
dp[cur][j]=0;
}
cur^=1;
}
rep(i,1,n) ckdel(ans,a[i]);
print(mul(ans,(mod+1)/2));
return 0;
}
数
进行简单关于等差数列的和式变换可以发现满足条件的 \((a,b,c,k)\) 需要满足(设 \(d\) 为公差) \(-k=(a-3d)(a+d)\)
那么设 \(x=3d-a,y=a+d\) 那么需要满足 \(4|(x+y)\) 且 \(y\ge\frac13 x\) ,讨论 \(y\) 在模 \(4\) 意义下的余数:
-
是奇数,对应 \(k\) 是模 \(4\) 余 \(3\) 的质数,否则分解方式为 \(0\) 或者大于 \(1\)
-
模 \(4\) 余 \(0\) ,对应 \(k\) 是 \(16\) 的倍数,这时 \(\frac k{16}\) 如果不是不为 \(1\) 的质数或者为 \(2\) 那么能通过任意分解质因数找到 \(y\ge x>\frac13 x\) 的两组解,那么自然是不合法的
-
模 \(4\) 余 \(2\) ,此时 \(x\equiv 2 \mod 4\Rightarrow 4|k\) ,同上 \(\frac{k}4\) 也是 \(1\) 或者奇质数
剩下的工作是若干次 \(\min \_25\) 筛,欢迎给这份代码加 \(\rm hack\) 数据哦!
Code Display
const int N=2e6+10;
namespace Prime_calculator{
int n,block,tot;
int id1[N],id2[N],val[N],g[N];
int pri[N],cnt,fl[N];
inline int get_id(int x){return x<=block?id1[x]:id2[n/x];}
inline void clear(){
n=block=tot=cnt=0;
memset(pri,0,sizeof(pri));
memset(fl,0,sizeof(fl));
memset(g,0,sizeof(g));
memset(val,0,sizeof(val));
memset(id1,0,sizeof(id1));
memset(id2,0,sizeof(id2));
}
inline int solve(int NN){
clear();
block=sqrt(n=NN);
for(int i=2;i<=block;++i){
if(!fl[i]) pri[++cnt]=i;
for(int j=1;j<=cnt&&i*pri[j]<=block;++j){
fl[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
for(int l=1,r;l<=n;l=r+1){
val[++tot]=n/l; r=n/val[tot];
g[tot]=val[tot]-1;
if(val[tot]<=block) id1[val[tot]]=tot;
else id2[r]=tot;
}
for(int i=1;i<=cnt;++i){
for(int j=1;pri[i]*pri[i]<=val[j];++j){
g[j]-=g[get_id(val[j]/pri[i])]-(i-1);
}
}
return g[1];
}
}
namespace Guass_counter{
int fl[N],cnt,pri[N],n;
int num1[N],num3[N],g1[N],g3[N],id1[N],id2[N];
int tot,block,val[N];
inline int get_id(int x){return x<=block?id1[x]:id2[n/x];}
inline int solve(int NN){
block=sqrt(n=NN);
for(int i=2;i<=block;++i){
if(!fl[i]){
pri[++cnt]=i;
num1[cnt]=num1[cnt-1]; num3[cnt]=num3[cnt-1];
if(i%4==3) num3[cnt]++;
if(i%4==1) num1[cnt]++;
}
for(int j=1;j<=cnt&&i*pri[j]<=block;++j){
fl[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
for(int l=1,r;l<=n;l=r+1){
val[++tot]=n/l; r=n/val[tot];
g1[tot]=(val[tot]-1)/4;
g3[tot]=(val[tot]+1)/4;
if(val[tot]<=block) id1[val[tot]]=tot;
else id2[r]=tot;
}
rep(i,2,cnt){
if(pri[i]%4==1){
for(int j=1;pri[i]*pri[i]<=val[j];++j){
int x=get_id(val[j]/pri[i]);
g1[j]-=g1[x]-num1[i-1];
g3[j]-=g3[x]-num3[i-1];
}
}else{
for(int j=1;pri[i]*pri[i]<=val[j];++j){
int x=get_id(val[j]/pri[i]);
g1[j]-=g3[x]-num3[i-1];
g3[j]-=g1[x]-num1[i-1];
}
}
}
return g3[get_id(n)];
}
}
inline bool check(int x,int y){
if((x+y)%4) return 0;
int d=(x+y)/4,a=y-d;
return a>0;
}
inline bool check(int n){
int cnt=0;
for(int i=1;cnt<=2&&i*i<=n;++i) if(n%i==0){
cnt+=check(i,n/i);
if(i!=n/i) cnt+=check(n/i,i);
}
return cnt==1;
}
signed main(){
freopen("number.in","r",stdin); freopen("number.out","w",stdout);
int n=read();
int ans=Guass_counter::solve(n)+Prime_calculator::solve(n/4)+Prime_calculator::solve(n/16);
print(ans); putchar('\n');
return 0;
}
矩阵
参考 \(q\le 10\) 的部分分发现本题基础想法应当为枚举 \(x\in[x_1,x_2]\) 并计算区间 \([y_1,y_2]\) 的最大值再统一取 \(\max\)
对 \(x\) 这维分治,在区间 \([L,R]\) 处理 \(L\le x_1\le mid=\frac{L+R}2<x_2\le R\) 的询问 \(\{[x_1,x_2]\}\),此时使用线段树维护 \(y\) 轴信息
处理 \([L,R]\) 时线段树上已经添加了 \([1,L-1]\) 的操作信息(若干次区间修改)
递归左侧;此时满足 \([L,mid]\) 的信息都在线段树上表示,倒序撤销之并维护线段树历史信息最大值,对于每个询问在左端点 \(x_1\) 查询 \([y_1,y_2]\) 的历史最大值,完成答案维护之后再将所有信息正序加入线段树
递归右半子区间,结束后并撤销操作信息,清空线段树历史最大值并顺序加入右半子区间信息,在询问右端点处再次进行历史最大值的回答,整个区间处理完成之后情况线段树上的历史最值
经过上述过程,区间内的询问处理完毕,线段树上 \([L,R]\) 的所有操作都得到了添加
实现上有一些细节:
其一是由于线段树维护历史最大值是将历史设置为每次询问一个版本而非每个 \(x\) 值进行操作结束后一次涌入,那么加入矩形信息需要先删后加,对应处理左半区间的倒序撤销操作需要先删加再添减
其二是注意在清空历史最大值时需要将历史最大值设置成当前的 \(\max\) 来满足实际含义,那么需要先下放区间修改的懒标记来保证子区间的 \(\max\) 是正确的
Code Display
const int N=2e5+10;
int n,m,Q,ans[N];
int qa[N],qb[N],qc[N],qd[N];
vector<tuple<int,int,int> >inc[N],red[N];
struct Seg{
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
int mx[N<<2],tag[N<<2],his[N<<2],mxtag[N<<2];
bool clr[N<<2],leaf[N<<2];
inline void build(int p,int l,int r){
if((leaf[p]=(l==r))) return ;
int mid=(l+r)>>1;
build(lson); build(rson);
return ;
}
inline void push_tag(int p,int v,int vmx){
ckmax(his[p],vmx+mx[p]);
ckmax(mxtag[p],vmx+tag[p]);
mx[p]+=v; tag[p]+=v;
return ;
}
inline void clear(int p){
if(!leaf[p]){
push_tag(ls,tag[p],mxtag[p]);
push_tag(rs,tag[p],mxtag[p]);
clr[p]=1;
}
his[p]=mx[p];
mxtag[p]=tag[p]=0;
}
inline void push_up(int p){
mx[p]=max(mx[ls],mx[rs]);
his[p]=max(his[ls],his[rs]);
}
inline void push_down(int p){
if(clr[p]){
clear(ls); clear(rs);
clr[p]=0;
}
push_tag(ls,tag[p],mxtag[p]);
push_tag(rs,tag[p],mxtag[p]);
mxtag[p]=tag[p]=0;
return ;
}
inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){
if(st<=l&&r<=ed) return push_tag(p,v,v);
int mid=(l+r)>>1; push_down(p);
if(st<=mid) upd(st,ed,v,lson);
if(ed>mid) upd(st,ed,v,rson);
return push_up(p);
}
inline int query(int st,int ed,int p=1,int l=1,int r=n){
if(st<=l&&r<=ed) return his[p];
int mid=(l+r)>>1,res=0; push_down(p);
if(st<=mid) ckmax(res,query(st,ed,lson));
if(ed>mid) ckmax(res,query(st,ed,rson));
return res;
}
}T;
inline void insert(int lin){
for(auto t:red[lin]) T.upd(get<0>(t),get<1>(t),get<2>(t));
for(auto t:inc[lin]) T.upd(get<0>(t),get<1>(t),get<2>(t));
}
inline void erase(int lin){
for(auto t:inc[lin]) T.upd(get<0>(t),get<1>(t),-get<2>(t));
for(auto t:red[lin]) T.upd(get<0>(t),get<1>(t),-get<2>(t));
}
inline void solve(int l,int r,vector<int> cur){
if(!cur.size()){
rep(i,l,r) insert(i);
return ;
}
if(l==r){
insert(l);
T.clear(1);
for(auto qid:cur) ckmax(ans[qid],T.query(qb[qid],qd[qid]));
return ;
}
int mid=(l+r)>>1;
vector<int> cro,Lef,Rig;
for(auto qid:cur){
if(qa[qid]<=mid&&qc[qid]>mid) cro.emplace_back(qid);
else if(qc[qid]<=mid) Lef.emplace_back(qid);
else Rig.emplace_back(qid);
}
solve(l,mid,Lef);
T.clear(1);
sort(cro.begin(),cro.end(),[&](const int x,const int y){return qa[x]>qa[y];});
int indic=0,siz=cro.size();
for(int i=mid;i>=l;--i){
while(indic<siz&&qa[cro[indic]]==i){
int id=cro[indic];
ckmax(ans[id],T.query(qb[id],qd[id])),++indic;
}
erase(i);
}
for(int i=l;i<=mid;++i) insert(i);
solve(mid+1,r,Rig);
sort(cro.begin(),cro.end(),[&](const int x,const int y){return qc[x]<qc[y];});
indic=0;
for(int i=r;i>mid;--i) erase(i);
T.clear(1);
for(int i=mid+1;i<=r;++i){
insert(i);
while(indic<siz&&qc[cro[indic]]==i){
int id=cro[indic];
ckmax(ans[id],T.query(qb[id],qd[id])),++indic;
}
}
return ;
}
signed main(){
freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout);
n=read(); m=read(); Q=read();
bool one=1;
for(int i=1;i<=m;++i){
int a=read(),b=read(),c=read(),d=read(),v=read();
inc[a].emplace_back(b,d,v);
red[c+1].emplace_back(b,d,-v);
}
vector<int> all(Q);
for(int i=1;i<=Q;++i){
qa[i]=read(),qb[i]=read(); qc[i]=read(); qd[i]=read();
all[i-1]=i;
}
T.build(1,1,n);
solve(1,n,all);
rep(i,1,Q) print(ans[i]);
return 0;
}