因为只有std,没有自我实现,所以是无码专区
主要是为了训练思维能力
solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dls的实现不太一样。
std可能也会带有博主自己的注释。
problem
有 n n n 个整数,第 i i i 个整数在 [ x i , y i ] [x_i,y_i] [xi,yi] 区间。
给定 m m m 个限制,形如 l i , r i , s i l_i,r_i,s_i li,ri,si ,要求第 l i l_i li 到 r i r_i ri 的数字加起来对 2 2 2 取模余数为 s i s_i si。
求有多少种整数序列满足上面限制,答案对 1 0 9 + 7 10^9+7 109+7 取模。
以及输出字典序最小的整数序列。
n ≤ 40 , m ≤ 100 , 0 ≤ x i ≤ y i ≤ 1 e 9 n\le 40,m\le 100,0\le x_i\le y_i\le 1e9 n≤40,m≤100,0≤xi≤yi≤1e9
1 s , 128 M B 1s,128MB 1s,128MB
my idea
因为限制是在模 2 2 2 意义下的,所以真的有关系的只是这个数是奇数还是偶数。
对于 50 % 50\% 50% 的数据点 n ≤ 20 n\le 20 n≤20,直接 2 n 2^n 2n 枚举每一位是选的奇数还是偶数,然后枚举所有限制判断是否合法,顺道可以记录一下字典序最小解及个数,时间复杂度 O ( 2 n m ) O(2^nm) O(2nm)。
将 [ l i , r i ] [l_i,r_i] [li,ri] 的限制转化为前缀和做差的限制,即 s u m ( r ) − s u m ( l − 1 ) sum(r)-sum(l-1) sum(r)−sum(l−1)。
将 s u m ( i ) sum(i) sum(i) 看作一个点,将一个限制看作 l i − 1 → r i l_i-1\rightarrow r_i li−1→ri 的有向边,边权为 s i s_i si。
这样会形成若干个相互独立的有向图。
考虑将这些图,以及图内一个点可能引发的若干条边合并起来,变成一条链。
i.e.
要求 [ 3 , 5 ] = 1 , [ 4 , 8 ] = 0 , [ 4 , 7 ] = 1 ⇒ 2 → 5 ( 1 ) , 3 → 8 ( 0 ) , 3 → 7 ( 1 ) [3,5]=1,[4,8]=0,[4,7]=1\Rightarrow 2\rightarrow 5(1),3\rightarrow 8(0),3\rightarrow 7(1) [3,5]=1,[4,8]=0,[4,7]=1⇒2→5(1),3→8(0),3→7(1)
⇒ 2 → 3 ( x ) → 5 ( x ⨁ 1 ) → 7 ( x ⨁ 1 ) → 8 ( x ) \Rightarrow 2\rightarrow 3(x)\rightarrow 5(x\bigoplus 1)\rightarrow 7(x\bigoplus1)\rightarrow 8(x) ⇒2→3(x)→5(x⨁1)→7(x⨁1)→8(x)
当最开始的一条边上的权确定时,整条链就确定了。
具体而言:直接枚举两两限制进行合并, [ l 1 , r 1 ] [ l 2 , r 2 ] , l 1 < l 2 < r 1 < r 2 ⇒ l 1 → l 2 → r 1 → r 2 [l_1,r_1][l_2,r_2],l_1
[l1,r1][l2,r2],l1<l2<r1<r2⇒l1→l2→r1→r2 。然后对于每个参与点进行限制的 d f s dfs dfs,开始跑每条边的边权。
如果到相同点的边权在取模 2 2 2 意义下不同,说明限制之间出现矛盾。
注意到并不是每个数都在这条链上,i.e.
4 , 6 , 1... 4,6,1... 4,6,1...
这些边只是起将限制紧密联系在一起的作用。
每两个点之间的原区间的方案数是可以通过 d p dp dp 计算的。
设 d p i , 0 / 1 : dp_{i,0/1}: dpi,0/1: 前 i i i 个数和为 0 / 1 0/1 0/1 (在 ( m o d 2 ) \pmod 2 (mod2) 的意义下)的方案数。
一个数的取值区间 [ l i , r i ] [l_i,r_i] [li,ri],无非可以划分为奇数 x x x 个,偶数 y y y 个。
选奇数会改变奇偶,选偶数则不改变。
d p i , 0 ← d p i − 1 , 0 × y + d p i − 1 , 1 × x d p i , 1 ← d p i − 1 , 0 × x + d p i − 1 , 1 × y dp_{i,0}\leftarrow dp_{i-1,0}\times y+dp_{i-1,1}\times x\\dp_{i,1}\leftarrow dp_{i-1,0}\times x+dp_{i-1,1}\times y dpi,0←dpi−1,0×y+dpi−1,1×xdpi,1←dpi−1,0×x+dpi−1,1×y
没有限制的区间就这么转移,一旦有类似与上面的边限制,去除掉不合法,继续转移即可。
貌似这样转移很困难w(゚Д゚)w
solution
首先, 考虑前缀和, 每个 [ l , r ] [l,r] [l,r] 的限制转化为 l − 1 l-1 l−1 到 r r r 两个前缀和之间的限制。
然后对所有限制进行连边。
每个连通块的奇偶性只有两种选择方法。【并不需要像我那么傻逼地去缩成一条链】
我们可以枚举每个连通块的奇偶性, 然后统计答案。
注意到, 如果一个连通块只有 1 1 1 个数,我们不需要枚举(放到后面去 d p dp dp)
因为这个位置的奇偶性不会影响到别的元素。
所以只要在枚举完每个大于等于 2 2 2 的连通块的奇偶性之后, 从右往左做一遍 d p dp dp, 记录一下当前前缀和的奇偶性是多少, 对应的方案数即可。
时间复杂度 O ( 2 n 2 n ) O(2^\frac{n}{2}n) O(22nn)。
std
#include
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
const int MAXN = 42 ;
const int mod = 1e9 + 7 ;
char buf[1000000] ;
int len ;
int p[MAXN], c[MAXN] ;
int L[MAXN], R[MAXN], odd[MAXN], even[MAXN] ;
int dp[MAXN][MAXN][2] ;
pair < int, int > nxt[MAXN][MAXN][2] ;
int ans[MAXN], tmp[MAXN] ;
int rt[MAXN], col[MAXN] ;
int idx[MAXN] ;
int use[MAXN] ;
int n, m ;
int T ;
int F ( int x ) { //带权并查集实现前缀和限制
if ( p[x] == x ) return x ;
int res = F ( p[x] ) ;
c[x] ^= c[p[x]] ;
return p[x] = res;
}
int upd ( int x, int y, int o, int n ) {
if ( dp[y][x][o] == 0 )
return 0 ;
int m = y - x ;
for ( int i = x ; i <= y ; ++ i ) {
tmp[i] = nxt[y][i][o].first ;
o = nxt[y][i][o].second ;
}
return 1 ;
}
void up ( int ok ) {
if ( !ok )
return ;
for ( int i = 1 ; i <= n ; ++ i ) {
if ( tmp[i] > ans[i] )
return ;
if ( tmp[i] < ans[i] ) {
for ( int j = 1 ; j <= n ; ++ j ) {
ans[j] = tmp[j] ;
}
return ;
}
}
}
void add ( int x ) {
if ( x / 10 )
add ( x / 10 ) ;
buf[len ++] = x % 10 + '0' ;
}
int main() {
freopen("parity.in", "r", stdin);
freopen("parity.out", "w", stdout);
scanf ( "%d%d", &n, &m ) ;
for ( int i = 0 ; i <= n ; ++ i ) {
p[i] = i ;
c[i] = 0 ;
use[i] = 0 ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( "%d%d", &L[i], &R[i] ) ;
int tmp = R[i] - L[i] + 1 ;
//计算i可选范围内奇数和偶数的个数
odd[i] = tmp / 2 + ( tmp % 2 == 1 && R[i] % 2 == 1 ) ;
even[i] = tmp / 2 + ( tmp % 2 == 1 && R[i] % 2 == 0 ) ;
}
for ( int i = 1 ; i <= n ; ++ i ) {
for ( int j = 1 ; j <= n + 1 ; ++ j ) {
dp[i][j][0] = dp[i][j][1] = 0 ;
nxt[i][j][0] = make_pair ( mod, mod ) ;
nxt[i][j][1] = make_pair ( mod, mod ) ;
}
}
//dp[l][r][0/1]:计算[l,r]区间内数的奇偶性
for ( int i = 1 ; i <= n ; ++ i ) {
dp[i][i + 1][0] = 1 ;
for ( int j = i ; j >= 1 ; -- j ) {
dp[i][j][0] = ( 1LL * dp[i][j + 1][1] * odd[j] + 1LL * dp[i][j + 1][0] * even[j] ) % mod ;
dp[i][j][1] = ( 1LL * dp[i][j + 1][0] * odd[j] + 1LL * dp[i][j + 1][1] * even[j] ) % mod ;
}
for ( int j = 1 ; j <= i ; ++ j ) {
if ( dp[i][j + 1][1] && odd[j] ) {
nxt[i][j][0] = min ( nxt[i][j][0], make_pair ( L[j] + ( L[j] % 2 == 0 ), 1 ) ) ;
}
if ( dp[i][j + 1][0] && even[j] ) {
nxt[i][j][0] = min ( nxt[i][j][0], make_pair ( L[j] + ( L[j] % 2 == 1 ), 0 ) ) ;
}
if ( dp[i][j + 1][0] && odd[j] ) {
nxt[i][j][1] = min ( nxt[i][j][1], make_pair ( L[j] + ( L[j] % 2 == 0 ), 0 ) ) ;
}
if ( dp[i][j + 1][1] && even[j] ) {
nxt[i][j][1] = min ( nxt[i][j][1], make_pair ( L[j] + ( L[j] % 2 == 1 ), 1 ) ) ;
}
}
}
int ok = 1 ;
for ( int i = 1 ; i <= m ; ++ i ) {
int u, v, w, x, y ;
scanf ( "%d%d%d", &u, &v, &w ) ;
-- u ;
x = F ( u ) ;
y = F ( v ) ;
use[u] = use[v] = 1 ;
if ( x != y ) {
if ( x < y ) swap ( x, y ) ;
p[x] = y ;
c[x] = ( c[u] - c[v] + 2 + w ) % 2 ;
} else if ( ( c[u] ^ c[v] ) != w ) ok = 0 ;
}
if ( !ok ) { //限制冲突 无解-1
buf[len ++] = '0' ;
buf[len ++] = '\n' ;
buf[len ++] = '-' ;
buf[len ++] = '1' ;
buf[len ++] = '\n' ;
return 0;
}
int cnt = 0, tot = 0, uns = 0;
for ( int i = 0 ; i <= n ; ++ i )
if ( use[i] ) {
if ( F ( i ) == i )
rt[cnt ++] = i;
idx[tot ++] = i;
} else
uns++;
int res = 0, OK = 0 ;
for ( int i = 1 ; i <= n ; ++ i )
ans[i] = mod ;
for ( int s = 0 ; s < 1 << cnt ; ++ s ) {
if ( cnt && rt[0] == 0 && s % 2 )
continue ;
col[0] = 0 ;
for ( int i = 0 ; i < cnt ; ++ i ) {
col[rt[i]] = s >> i & 1 ;
}
int ans = 1, j = 0, ok = 1 ;
for ( int o = ( rt[0] == 0 ) ; o < tot ; ++ o ) {
int i = idx[o], x = p[i] ;
if ( x != i )
col[i] = col[x] ^ c[x] ^ c[i] ;
ans = 1LL * ans * dp[i][j + 1][col[j] ^ col[i]] % mod ;
ok &= upd ( j + 1, i, col[j] ^ col[i], j ) ;
if ( !ok )
break ;
j = i ;
}
if ( ok && j < n ) {
ans = 1LL * ans * ( dp[n][j + 1][0] + dp[n][j + 1][1] ) % mod ;
int a = upd ( j + 1, n, 0, j ) ;
up ( ok && a ) ;
int b = upd ( j + 1, n, 1, j ) ;
up ( ok && b ) ;
ok &= a | b ;
} else
up ( ok ) ;
if ( ok )
OK = 1 ;
res = ( res + ans ) % mod ;
}
if ( !OK ) {
buf[len ++] = '0' ;
buf[len ++] = '\n' ;
buf[len ++] = '-' ;
buf[len ++] = '1' ;
buf[len ++] = '\n' ;
} else {
add ( res ) ;
buf[len ++] = '\n' ;
for ( int i = 1 ; i <= n ; ++ i ) {
add ( ans[i] ) ;
buf[len ++] = i < n ? ' ' : '\n' ;
}
}
buf[len] = 0;
printf("%s", buf);
}