题外话
写篇题解攒rp
A
题意:
给定长度为(n)的序列({A_i}),一组((x,y,z)(1le x<y<zle n))合法,当且仅当在区间([x,z]),(A_x,A_y,A_z)均只出现一次
做法
对(z)进行扫描线,同时维护每个(x)右边有效(y)的数量(B_x)。
令(last_i)为(i)前面离(i)最近的与(A_i)相同的数字。
在枚举到(i)时
- 查询(j<i),(B_j>0)的(B_j)之和。
- 初始化(B_i=0),令(B_{last_i}=-infty)
- 对(xin (last_{last_i},last_i)的)(B_x-1);对(xin(last_i,i))的(B_x+1)。
以上操作均可以用线段树优化。
总复杂度(O(nlogn))。
#include<bits/stdc++.h>
typedef long long LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=2e5+9,mod=998244353,inf=0x3f3f3f3f;
LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
}return x*f;
}
void Chkmin(LL &x,LL y){
if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
if(y>x) x=y;
}
LL add(LL x,LL y){
return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
LL ret(1); while(b){
if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
}return ret;
}
namespace Sgt{
LL sum[maxn<<2],num[maxn<<2],son[maxn<<2][2],tag[maxn<<2];
void Update(LL nw){
sum[nw]=sum[nw<<1]+sum[nw<<1|1];
num[nw]=num[nw<<1]+num[nw<<1|1];
}
void Addpush(LL nw,LL v){
tag[nw]+=v;
sum[nw]+=num[nw]*v;
}
void Pushdown(LL nw){
LL x(tag[nw]);
Addpush(nw<<1,x); Addpush(nw<<1|1,x);
tag[nw]=0;
}
void Modify(LL nw,LL l,LL r,LL lt,LL rt,LL v){
if(lt<=l && rt>=r){
Addpush(nw,v);
return;
}
Pushdown(nw);
LL mid(l+r>>1);
if(lt<=mid) Modify(nw<<1,l,mid,lt,rt,v);
if(rt>mid) Modify(nw<<1|1,mid+1,r,lt,rt,v);
Update(nw);
}
void Insert(LL nw,LL l,LL r,LL x){
if(l==r){
num[nw]++; assert(!sum[nw]);
return;
}
Pushdown(nw);
LL mid(l+r>>1);
if(x<=mid) Insert(nw<<1,l,mid,x);
else Insert(nw<<1|1,mid+1,r,x);
Update(nw);
}
void Del(LL nw,LL l,LL r,LL x){
if(l==r){
num[nw]--; sum[nw]=0;
return;
}
Pushdown(nw);
LL mid(l+r>>1);
if(x<=mid) Del(nw<<1,l,mid,x);
else Del(nw<<1|1,mid+1,r,x);
Update(nw);
}
LL Query(LL nw,LL l,LL r,LL lt,LL rt){
if(lt<=l && rt>=r) return sum[nw];
Pushdown(nw);
LL mid(l+r>>1),ret(0);
if(lt<=mid) ret+=Query(nw<<1,l,mid,lt,rt);
if(rt>mid) ret+=Query(nw<<1|1,mid+1,r,lt,rt);
return ret;
}
void Write(LL nw,LL l,LL r){
if(l==r){
printf("(%lld,%lld)",sum[nw],num[nw]);
return;
}Pushdown(nw);
LL mid(l+r>>1);
Write(nw<<1,l,mid); Write(nw<<1|1,mid+1,r);
}
}
LL n;
LL a[maxn],pre[maxn],last[maxn];
int main(){
n=Read();
for(LL i=1;i<=n;++i) a[i]=Read();
LL ret(0);
for(LL i=1;i<=n;++i){
LL c(a[i]);
LL x(last[c]);
pre[i]=x;
ret+=Sgt::Query(1,1,n,x+1,i);
if(x){
Sgt::Del(1,1,n,x);
}
Sgt::Insert(1,1,n,i);
if(pre[x]+1<=x-1){
Sgt::Modify(1,1,n,pre[x]+1,x-1,-1);
}
if(pre[i]+1<=i-1){
Sgt::Modify(1,1,n,pre[i]+1,i-1,1);
}
last[c]=i;
}
printf("%lld
",ret);
return 0;
}
B
题意:
给定(n,K)及一张有向图,保证(i<j,ilongleftarrow j)的边只有(K)条。
再给定(n)个点的状态,空 or 起点 or 终点(起点数量与终点数量相同)。
一组合法的方案为:起点与终点进行配对,之后每个起点向对应的终点走,可经过一个点多次,要求每条边被恰好经过一次。
保证至少有一组合法方案。
对合法方案进行计数。
多测。
(Kle 4,sum n^2le 20000)。
做法
建立超级源汇点(U)。
对于每个起点(i),(Ulongrightarrow i)。
对于每个终点(i),(ilongrightarrow U)。
对于原题意的每种方案,对应新图的一条欧拉回路。
对于有向图欧拉回路计数,可以使用( ext{BEST})定理。
总复杂度(sum n^3)(与(K)无关),如果有与(K)有关的做法,欢迎一起交流/kel。
#include<bits/stdc++.h>
using namespace std;
typedef double dl;
#define opt operator
#define pb push_back
const int MAX=201;
const int mod=1e9+7;
int add(int x,int y){
return x+=y,x>=mod?x-mod:x;
}
int dec(int x,int y){
return x-=y,x<0?x+mod:x;
}
int mul(int x,int y){
return 1ll*x*y%mod;
}
int Pow(int base,int b){
int ret(1); while(b){
if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
}return ret;
}
int Inv(int x){
return Pow(x,mod-2);
}
struct mat{
int N;
int A[MAX][MAX];
void Init(int n,int op=0){
N=n;
memset(A,0,sizeof(A));
for(int i=1;i<=N;++i) A[i][i]=op;
}
mat opt * (const mat B){
mat ret; ret.Init(N,0);
for(int i=1;i<=N;++i){
for(int k=1;k<=N;++k){
for(int j=1;j<=N;++j){
ret.A[i][j]=add(ret.A[i][j],mul(A[i][k],B.A[k][j]));
}
}
}return ret;
}
int Getdet(){
int ret(1);
for(int i=1;i<=N;++i){
for(int j=i;j<=N;++j) if(A[j][i]){
if(j^i) ret=dec(0,ret);
std::swap(A[i],A[j]);
break;
}
int inv(Inv(A[i][i]));
for(int j=i+1;j<=N;++j){
int tmp=mul(inv,A[j][i]);
for(int k=i;k<=N;++k){
A[j][k]=dec(A[j][k],mul(tmp,A[i][k]));
}
}
}
for(int i=1;i<=N;++i) ret=mul(ret,A[i][i]);
return ret;
}
void Write(){
for(int i=1;i<=N;++i){
for(int j=1;j<=N;++j){
printf("%d ",A[i][j]<=100?A[i][j]:A[i][j]-mod);
}puts("");
}puts("-------------------");
}
};
int n,K,T;
int star[MAX],en[MAX],fac[MAX],V[MAX][MAX];
char s[MAX],start[MAX];
void Init2(){
scanf("%d%d",&n,&K);
fac[0]=1; for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
scanf(" %s",start+1);
for(int i=1;i<=n;++i) star[i]=start[i]=='S';
for(int i=1;i<=n;++i) en[i]=start[i]=='R';
for(int i=1;i<=n;++i){
static char s[MAX];
scanf(" %s",s+1);
for(int j=1;j<=n;++j) V[i][j]=s[j]-'0';
}
}
void Solve(){
static int degout[MAX],degin[MAX];
memset(degout,0,sizeof(degout));
memset(degin,0,sizeof(degin));
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
degout[i]+=V[i][j];
degin[j]+=V[i][j];
}
}
for(int i=1;i<=n;++i){
degout[i]+=en[i];
}
for(int i=1;i<=n;++i){
degin[i]+=star[i];
}
static int id[MAX];
memset(id,0,sizeof(id));
int nod(0);
for(int i=1;i<=n;++i) if(degout[i] || degin[i]){
id[i]=++nod;
}
static mat M;
M.Init(nod,0);
for(int i=1;i<=n;++i)if(id[i]){
int nw(id[i]);
int &x=M.A[nw][nw];
x=add(x,degout[i]);
for(int j=1;j<=n;++j) if(V[i][j]){
int nxt(id[j]);
assert(nxt);
int &y=M.A[nw][nxt];
y=dec(y,1);
}
}
int ret(M.Getdet());
for(int i=1;i<=n;++i) if(degout[i])
ret=mul(ret,fac[degout[i]-1]);
printf("%d
",ret);
}
void Init1(){
scanf("%d",&T);
while(T--){
Init2();
Solve();
}
}
int main(){
Init1();
return 0;
}
C
题意:
给定(n imes n)的一个草坪(一个位置要么为草要么为石头),求以下子图个数
(1)子图四连通,且全部为草。
(2)若存在两点((x_1,y)(x_2,y)(x_1le x_2)),那么对于((x,y)(xin[x_1,x_2]))也应在子图内。
(3)若存在两点((x,y_1)(x,y_2)(y_1le y_2)),那么对于((x,y)(yin[y_1,y_2]))也应在子图内。
(nle 150)
做法
用((l_x,r_x)ldots(l_y,r_y)(xle y))((xle y))描述一组合法方案。
对于(iin[x,y)),((l_i,r_i))应与((l_{i+1},r_{i+1}))有交。
(l_i(iin[x,y]))应先递减再递增(非严格)
(r_i(iin[x,y]))应先递增再递减(非严格)
令(f_{i,j,k,0/1,0/1})为(l_i=j,r_i=k),目前左边界是否未递减,目前右边界是否未递增。
暴力转移,复杂度为(O(n^5))(详见以下代码)。
考虑前缀和,可以优化至(O(n^3))(由于有(2^6)倍常数,需要对减少取模次数,依次优化常数)。
(O(n^5))
#include<bits/stdc++.h>
typedef int LL;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=151,mod=1e9+7,inf=0x3f3f3f3f;
LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
}return x*f;
}
void Chkmin(LL &x,LL y){
if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
if(y>x) x=y;
}
LL add(LL x,LL y){
return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
LL ret(1); while(b){
if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
}return ret;
}
LL n;
LL f[maxn][maxn][maxn][2][2],mark[maxn][maxn];
char s[maxn][maxn];
bool Check(LL l1,LL r1,LL l2,LL r2){
return std::max(l1,l2)<=std::min(r1,r2);
}
int main(){
n=Read();
for(LL i=1;i<=n;++i){
scanf(" %s",s[i]+1);
}
LL ret(0);
for(LL i=1;i<=n;++i){
memset(mark,0,sizeof(mark));
for(LL l=1;l<=n;++l){
for(LL r=l;r<=n && s[i][r]=='G';++r){
f[i][l][r][1][1]=1;
mark[l][r]=1;
}
}
for(LL l1=1;l1<=n;++l1){
for(LL r1=l1;r1<=n;++r1)if(mark[l1][r1]){
for(LL l2=1;l2<=n;++l2){
for(LL r2=l2;r2<=n;++r2) if(Check(l1,r1,l2,r2)){
LL ff1(l1<l2),ff2(r1>r2);
LL flag1(l1<=l2),flag2(r1>=r2);
for(LL op0=0;op0<2;++op0)if((op0&ff1)==ff1){
for(LL op1=0;op1<2;++op1)if((op1&ff2)==ff2){
f[i][l1][r1][op0&flag1][op1&flag2]=add(f[i][l1][r1][op0&flag1][op1&flag2],f[i-1][l2][r2][op0][op1]);
}
}
}
}
}
}
for(LL l=1;l<=n;++l){
for(LL r=l;r<=n;++r){
LL tmp(0);
for(LL op0=0;op0<2;++op0){
for(LL op1=0;op1<2;++op1){
ret=add(ret,f[i][l][r][op0][op1]);
tmp=add(tmp,f[i][l][r][op0][op1]);
}
}
}
}
}
printf("%d
",ret);
return 0;
}
(O(n^3))
#include<bits/stdc++.h>
typedef int LL;
typedef long long ll;
typedef double dl;
#define opt operator
#define pb push_back
#define pii std::pair<LL,LL>
const LL maxn=151,mod=1e9+7,inf=0x3f3f3f3f;
LL Read(){
LL x(0),f(1); char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') f=-1; c=getchar();
}
while(c>='0' && c<='9'){
x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
}return x*f;
}
void Chkmin(LL &x,LL y){
if(y<x) x=y;
}
void Chkmax(LL &x,LL y){
if(y>x) x=y;
}
LL add(LL x,LL y){
return x+=y,x>=mod?x-mod:x;
}
LL dec(LL x,LL y){
return x-=y,x<0?x+mod:x;
}
LL mul(LL x,LL y){
return 1ll*x*y%mod;
}
LL Pow(LL base,LL b){
LL ret(1); while(b){
if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
}return ret;
}
LL n;
LL mark[maxn][maxn];
ll g[2][2][maxn][maxn],f[maxn][maxn][maxn][2][2];
char s[maxn][maxn];
bool Check(LL l1,LL r1,LL l2,LL r2){
return std::max(l1,l2)<=std::min(r1,r2);
}
void Fir(LL nw,LL op0,LL op1){
memset(g[op0][op1],0,sizeof(g[op0][op1]));
for(LL i=1;i<=n;++i){
for(LL j=1;j<=n;++j){
ll ret(g[op0][op1][i-1][j]+g[op0][op1][i][j-1]-g[op0][op1][i-1][j-1]+f[nw][i][j][op0][op1]);
g[op0][op1][i][j]=(ret%mod+mod)%mod;
}
}
}
ll Query(LL op0,LL op1,LL l_l,LL l_r,LL r_l,LL r_r){
ll ret(g[op0][op1][l_r][r_r]-g[op0][op1][l_l-1][r_r]-g[op0][op1][l_r][r_l-1]+g[op0][op1][l_l-1][r_l-1]);
return ret;
}
void Fir(LL nw){
for(LL op0=0;op0<2;++op0){
for(LL op1=0;op1<2;++op1){
Fir(nw,op0,op1);
}
}
}
int main(){
double ret1(clock());
n=Read();
for(LL i=1;i<=n;++i){
scanf(" %s",s[i]+1);
}
LL ret(0);
for(LL i=1;i<=n;++i){
memset(mark,0,sizeof(mark));
for(LL l=1;l<=n;++l){
for(LL r=l;r<=n && s[i][r]=='G';++r){
f[i][l][r][1][1]=1;
mark[l][r]=1;
}
}
Fir(i-1);
for(LL l1=1;l1<=n;++l1){
for(LL r1=l1;r1<=n;++r1)if(mark[l1][r1]){
for(LL ff1=0;ff1<2;++ff1){
for(LL ff2=0;ff2<2;++ff2){
for(LL flag1=0;flag1<2;++flag1){
if(!flag1 && ff1) continue;
for(LL flag2=0;flag2<2;++flag2){
if(!flag2 && ff2) continue;
LL l_l(1),l_r(n);
LL r_l(1),r_r(n);
if(ff1) Chkmax(l_l,l1+1); else Chkmin(l_r,l1);
if(flag1) Chkmax(l_l,l1); else Chkmin(l_r,l1-1);
if(ff2) Chkmin(r_r,r1-1); else Chkmax(r_l,r1);
if(flag2) Chkmin(r_r,r1); else Chkmax(r_l,r1+1);
Chkmin(l_r,r1);
Chkmax(r_l,l1);
if(l_l>l_r || r_l>r_r) continue;
for(LL op0=0;op0<2;++op0)if((op0&ff1)==ff1){
for(LL op1=0;op1<2;++op1)if((op1&ff2)==ff2){
f[i][l1][r1][op0&flag1][op1&flag2]+=Query(op0,op1,l_l,l_r,r_l,r_r);
}
}
}
}
}
}
}
}
for(LL l=1;l<=n;++l){
for(LL r=l;r<=n;++r){
for(LL op0=0;op0<2;++op0){
for(LL op1=0;op1<2;++op1){
ll &x(f[i][l][r][op0][op1]);
x=(x%mod+mod)%mod;
}
}
}
}
for(LL l=1;l<=n;++l){
for(LL r=l;r<=n;++r){
for(LL op0=0;op0<2;++op0){
for(LL op1=0;op1<2;++op1){
ret=add(ret,f[i][l][r][op0][op1]);
}
}
}
}
}
printf("%d
",ret);
return 0;
}