20220627
- rk 13/45, 56+60+2=118
- max:96, 100, 82, 92+100+2=194
我在打毛啊 —— F91
Number
每个数只有三种状态:卡上界、卡下界、都不卡
考虑从高到低 dp:设 \(f[i,s]\) 为前 \(i\) 位,数的状态为 \(s\) 的方案数,转移枚举下一位每个数填啥,位运算优化成 \(O(6^{n}\log v)\)
第三种状态的转移不受限制,填 \(0,1\) 均可。预处理 \(g[s,t]\) 表示要求 \(s\) 中数填 \(0\),\(t\) 中数填 \(1\),其余数任意的方案数,这样只需要枚举卡上/下界的数填啥,直接用 \(g\) 转移。每位只有 \(5\) 种转移(卡上/下界 \(\times\) 填 \(0/1\),都不卡),时间复杂度 \(O(5^{n}\log v)\)
还没有用 \(l,r\) 随机的性质。若当前为卡上界,那么下一位有 \(\frac{1}{2}\) 的概率是 \(0\)(只能填 \(0\)),\(\frac{1}{2}\) 的概率是 \(0\)(填 \(0,1\) 均可),即前两种状态期望有 \(\frac{3}{2}\) 种转移,每一位期望有 \(4\) 种,只枚举有用的转移即可做到 \(O(4^{n}\log v)\)
注意 dfs 填数非常慢,需要精细实现
code
const int U2 = 1<<11, U3 = 177147, U4 = 1<<22;
int n,ss,mp2[U2],mp4[U4],mp43[U4],is1[U4],is2[U4];
bool vis[U2];
LL a[11],b[11];
mint ans,f[U2][U2],g[U3],h[U3];
Vi vld;
void dfs0(int u,int s,int t,int x) {
if( u == n ) return f[s][t] += vis[x], void();
dfs0(u+1,s,t,x), dfs0(u+1,s,t,x|1<<u),
dfs0(u+1,s|1<<u,t,x|1<<u), dfs0(u+1,s,t|1<<u,x);
}
/*
mint dp(int u,int s) {
if( !~u ) return 1;
mint &res = g[u][mp43[s]];
if( res.x ) return res;
int is=0; Rep(i,0,n) is |= (((s>>2*i&3)==1?a[i]:b[i])>>u&1) << i;
for(int i = mp4[s], j = i&is; ; j = j-1&i&is) {
if( f[j][i^j].x ) {
res += f[j][i^j] * dp(u-1,s&~mp2[(i^j)&is]);
// int t = 0;
// Rep(k,0,n) if( int x = s>>2*k&3 )
// if( ((x==1?a[k]:b[k])>>u&1) == (j>>k&1) ) t |= x<<2*k;
// res += f[j][i^j] * dp(u-1,t);
}
if( !j ) break;
}
return res;
}
*/
signed main() { freopen("number.in","r",stdin); freopen("number.out","w",stdout);
io>>n; Rep(i,0,n) io>>a[i]>>b[i], --a[i], ss |= (a[i]>=0)<<i;
Rep(i,0,1<<n) vis[i] = read<char>()-'0';
dfs0(0,0,0,0);
Rep(s,0,1<<n) mp2[s] = mp2[s>>1]<<2 | (s&1)*3;
Rep(s,0,1<<2*n) {
mp4[s] = mp4[s>>2]<<1 | !!(s&3),
is1[s] = is1[s>>2]<<1 | ((s&3)==1),
is2[s] = is2[s>>2]<<1 | ((s&3)==2);
if( !~mp43[s>>2] || (s&3) == 3 ) mp43[s] = -1;
else {
mp43[s] = mp43[s>>2]*3 + (s&3);
if( (ss&mp4[s]) == mp4[s] ) vld.pb(s);
}
}
for(int s : vld) {
int is=0; Rep(i,0,n) is |= (((s>>2*i&3)==1?a[i]:b[i])&1) << i;
for(int i = mp4[s], j = i&is; ; j = j-1&i&is) {
g[mp43[s]] += f[j][i^j];
if( !j ) break;
}
}
int tom=0;
For(u,1,59) {
memcpy(h,g,sizeof g);
int as = 0, bs = 0;
Rep(i,0,n) as |= (a[i]>>u&1) << i, bs |= (b[i]>>u&1) << i;
for(int s : vld) {
int is = (is1[s]&as) | (is2[s]&bs);
uLL res = 0;
for(int i = mp4[s], j = is; ; j = j-1&is) {
++tom;
res += (uLL)f[j][i^j].x * h[mp43[s&~mp2[(i^j)&is]]].x;
if( !j ) break;
}
g[mp43[s]] = res;
}
}
for(int s = ss; ; s = s-1&ss) {
int t=0; Rep(i,0,n) t |= (s>>i&1?1:2) << i*2;
__builtin_parity(s) ? ans-=g[mp43[t]] : ans+=g[mp43[t]];
if( !s ) break;
}
cerr<<tom<<endl;
io<<ans.x;
return 0;
}
Light
原题,当时又双叒叕没改。赛时胡出了非常复杂的被卡常做法还没调出来
障碍很稀疏,可以考虑抠出无障碍的极长横条和竖条。记 \(f/g[i,j]\) 为在点 \((i,j)\),方向为横/竖时的最短路。容易发现对于一个横条上的点,\(f[i,j]\) 是相等的。把这些横/竖条看成点,交点看成边做 \(0/1\) bfs,使用主席树优化建图可以 \(O((n+k)\log(n+k))\) 算出每个横/竖条的最短路
考虑扫描线求答案,需要特殊处理的部分是交点。注意到 \(|f[i,j]-g[i,j]|\le1\),使用线段树维护最小值和个数就能求出有多少个点的最短路需要 \(-1\)
还有更巧妙的做法。记 \(\{f[i,j],g[i,j]\}=\{x,x+1\}\),考虑构造贡献系数 \(v_{x}+v_{x+1}=x^{2}\Rightarrow v_{x}=\frac{x(x-1)}{2}\)(事实上对于任意由距离给代价的方式都能找到满足条件的 \(v\)),答案即为 每段的长度乘距离的贡献系数 之和,可以 \(O(n+k)\) 统计
code
typedef pair<int,int> Pii;
const int N = 1e5+5, M = N*128;
int n,m,ind,dis[M];
mint ans;
set<int> a[N],b[N];
vector<pair<int,int>> c[N],d[N];
map<int,int> idx[N],idy[N];
struct {
int m,head[M],nxt[M*2],to[M*2]; bool w[M*2];
void clr() { m = 1, memset(head+1,0,sizeof(int)*ind); }
void add(int x,int y,int z)
{ if( x && y ) nxt[++m] = head[x], to[m] = y, w[m] = z, head[x] = m; }
void bfs() {
static deque<int> q;
memset(dis+1,0x3f,sizeof(int)*ind);
dis[idy[1][1]] = 0, q.pb(idy[1][1]);
while( sz(q) ) {
int u = q.front(); q.pop_front();
for(int i = head[u], v; v = to[i], i; i = nxt[i])
if( dis[u]+w[i] < dis[v] )
dis[v] = dis[u]+w[i], w[i] ? q.pb(v) : q.emplace_front(v);
}
}
} g;
#define ls (u<<1)
#define rs (u<<1|1)
#define mid (l+r>>1)
struct {
Pii t[N*4];
void bld(int u=1,int l=1,int r=n) {
t[u] = {0,0};
if( l == r ) return;
bld(ls,l,mid), bld(rs,mid+1,r);
}
void ins(int p,int x,int u=1,int l=1,int r=n) {
t[u].fi = ++ind, t[u].se = ++ind;
if( l == r ) return g.add(t[u].fi,x,0), g.add(x,t[u].se,0);
p<=mid ? ins(p,x,ls,l,mid) : ins(p,x,rs,mid+1,r),
g.add(t[u].fi,t[ls].fi,0), g.add(t[u].fi,t[rs].fi,0),
g.add(t[ls].se,t[u].se,0), g.add(t[rs].se,t[u].se,0);
}
void ers(int p,int u=1,int l=1,int r=n) {
if( l == r ) return;
p<=mid ? ers(p,ls,l,mid) : ers(p,rs,mid+1,r);
if( !t[ls].fi && !t[rs].fi ) t[u] = {0,0};
else
t[u].fi = ++ind, t[u].se = ++ind;
g.add(t[u].fi,t[ls].fi,0), g.add(t[u].fi,t[rs].fi,0),
g.add(t[ls].se,t[u].se,0), g.add(t[rs].se,t[u].se,0);
}
void qry(int ql,int qr,int x,int u=1,int l=1,int r=n) {
if( qr < l || r < ql ) return;
if( ql <= l && r <= qr ) return g.add(x,t[u].fi,1), g.add(t[u].se,x,1);
qry(ql,qr,x,ls,l,mid), qry(ql,qr,x,rs,mid+1,r);
}
} seg;
#undef ls
#undef rs
#undef mid
void MAIN() {
io>>n>>m; For(i,1,m, x,y) io>>x>>y, a[x].emplace(y), b[y].emplace(x);
For(i,1,n)
a[i].emplace(0), a[i].emplace(n+1), b[i].emplace(0), b[i].emplace(n+1);
seg.bld();
For(x,1,n) for(auto i = a[x].begin(), j = next(i); j != a[x].end(); i = j++)
if( *i < *j ) idx[x][*i+1] = ++ind, c[*i+1].pb(x,ind), d[*j].pb(x,ind);
For(y,1,n) {
for(auto &i : c[y] ) seg.ins(i.fi,i.se);
for(auto &i : d[y] ) seg.ers(i.fi);
for(auto i = b[y].begin(), j = next(i); j != b[y].end(); i = j++)
if( *i < *j ) seg.qry(*i+1,*j-1,idy[*i+1][y]=++ind);
}
g.bfs();
For(x,1,n) for(auto i = a[x].begin(), j = next(i); j != a[x].end(); i = j++)
if( *i < *j ) {
int d = dis[idx[x][*i+1]];
if( d < inf ) ans += d*(d-1ll)/2 * mint(*j-*i-1);
}
For(y,1,n) for(auto i = b[y].begin(), j = next(i); j != b[y].end(); i = j++)
if( *i < *j ) {
int d = dis[idy[*i+1][y]];
if( d < inf ) ans += d*(d-1ll)/2 * mint(*j-*i-1);
}
cout<<ans.x<<endl;
} signed main() {
#ifdef FS
freopen("light2.in","r",stdin); freopen("b.out","w",stdout);
#else
freopen("light.in","r",stdin); freopen("light.out","w",stdout);
#endif
ios::sync_with_stdio(0);cout.tie(0);
int mx=0;
int T=read(); while( T-- ) {
MAIN();
ckmax(mx,ind);
For(i,0,n+1)
a[i].clear(), b[i].clear(), c[i].clear(), d[i].clear(),
idx[i].clear(), idy[i].clear();
g.clr(), ind = ans.x = 0;
}
cerr<<mx<<endl;
return 0;
}