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;
}

Game

posted @ 2022-06-28 06:53  401rk8  阅读(15)  评论(0编辑  收藏  举报