因为只有std,没有自我实现,所以是无码专区
主要是为了训练思维能力
solution才是dls正解,但是因为只有潦草几句,所以大部分会有我自己基于正解上面的算法实现过程,可能选择的算法跟std中dls的实现不太一样。
std可能也会带有博主自己的注释。
problem
有 nnn 个整数,第 iii 个整数在 [xi,yi][x_i,y_i][xi,yi] 区间。
给定 mmm 个限制,形如 li,ri,sil_i,r_i,s_ili,ri,si ,要求第 lil_ili 到 rir_iri 的数字加起来对 222 取模余数为 sis_isi。
求有多少种整数序列满足上面限制,答案对 109+710^9+7109+7 取模。
以及输出字典序最小的整数序列。
n≤40,m≤100,0≤xi≤yi≤1e9n\le 40,m\le 100,0\le x_i\le y_i\le 1e9n≤40,m≤100,0≤xi≤yi≤1e9
1s,128MB1s,128MB1s,128MB
my idea
因为限制是在模 222 意义下的,所以真的有关系的只是这个数是奇数还是偶数。
对于 50%50\%50% 的数据点 n≤20n\le 20n≤20,直接 2n2^n2n 枚举每一位是选的奇数还是偶数,然后枚举所有限制判断是否合法,顺道可以记录一下字典序最小解及个数,时间复杂度 O(2nm)O(2^nm)O(2nm)。
将 [li,ri][l_i,r_i][li,ri] 的限制转化为前缀和做差的限制,即 sum(r)−sum(l−1)sum(r)-sum(l-1)sum(r)−sum(l−1)。
将 sum(i)sum(i)sum(i) 看作一个点,将一个限制看作 li−1→ril_i-1\rightarrow r_ili−1→ri 的有向边,边权为 sis_isi。
这样会形成若干个相互独立的有向图。
考虑将这些图,以及图内一个点可能引发的若干条边合并起来,变成一条链。
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)
当最开始的一条边上的权确定时,整条链就确定了。
具体而言:直接枚举两两限制进行合并,[l1,r1][l2,r2],l1<l2<r1<r2⇒l1→l2→r1→r2[l_1,r_1][l_2,r_2],l_1<l_2<r_1<r_2\Rightarrow l_1\rightarrow l_2\rightarrow r_1\rightarrow r_2[l1,r1][l2,r2],l1<l2<r1<r2⇒l1→l2→r1→r2。
然后对于每个参与点进行限制的 dfsdfsdfs,开始跑每条边的边权。
如果到相同点的边权在取模 222 意义下不同,说明限制之间出现矛盾。
注意到并不是每个数都在这条链上,i.e.
4,6,1...4,6,1...4,6,1...
这些边只是起将限制紧密联系在一起的作用。
每两个点之间的原区间的方案数是可以通过 dpdpdp 计算的。
设 dpi,0/1:dp_{i,0/1}:dpi,0/1: 前 iii 个数和为 0/10/10/1 (在(mod2)\pmod 2(mod2) 的意义下)的方案数。
一个数的取值区间 [li,ri][l_i,r_i][li,ri],无非可以划分为奇数 xxx 个,偶数 yyy 个。
选奇数会改变奇偶,选偶数则不改变。
dpi,0←dpi−1,0×y+dpi−1,1×xdpi,1←dpi−1,0×x+dpi−1,1×ydp_{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−1l-1l−1 到 rrr 两个前缀和之间的限制。
然后对所有限制进行连边。
每个连通块的奇偶性只有两种选择方法。【并不需要像我那么傻逼地去缩成一条链】
我们可以枚举每个连通块的奇偶性, 然后统计答案。
注意到, 如果一个连通块只有 111 个数,我们不需要枚举(放到后面去 dpdpdp)
因为这个位置的奇偶性不会影响到别的元素。
所以只要在枚举完每个大于等于 222 的连通块的奇偶性之后, 从右往左做一遍 dpdpdp, 记录一下当前前缀和的奇偶性是多少, 对应的方案数即可。
时间复杂度 O(2n2n)O(2^\frac{n}{2}n)O(22nn)。
std
#include <bits/stdc++.h>
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 ) { //限制冲突 无解-1buf[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;} elseuns++;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 ;} elseup ( 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);
}