日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[2021-07-19 内测NOIP] 操作(状压DP),异或(字典树),等级(线段树),矩阵(DP)

發布時間:2023/12/3 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [2021-07-19 内测NOIP] 操作(状压DP),异或(字典树),等级(线段树),矩阵(DP) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[2021-07-19 內測] NOIP

  • 操作
    • description
    • solution
    • code
  • 異或
    • description
    • solution
    • code
  • 等級
    • description
    • solution
    • code
  • 矩陣
    • description
    • solution
    • code

操作

description

有n堆石子,每堆石子都有一定的數量,第i堆石子的數量用Ai表示。

任意兩堆石子均可合并,一堆石子也可 以分成兩堆非空的石子。

一次合并或一次分裂都算一次操作。

經過若干次操作以后,石子還剩m堆,告訴你m堆石子的數量Bi,問至少經歷了幾次操作。

Input

多組數據,第一行一個正整數T表示數據組數,對于每組數據:

第一行,首先一個數n,接下來是n個正整數表示A1~An。

第二行,首先一個數m,接下來是m個正整數表示B1~Bm。

保證A數組的和等于B數組的和。

Output

每組數據一個整數,表示至少經歷了多少次操作,保證答案在int范圍之內。

Example

standard input

1

1 6

3 1 2 3

standard output

2

Note

對于100%的數據,1 ≤ n,m ≤ 10,1 ≤ Ai,Bi ≤ 50,1 ≤ T ≤ 5

solution

法一:搜索加剪枝,上限是O(nn)O(n^n)O(nn),但肯定跑不滿

法二:狀壓DPDPDP

合并某些AAA中的數,然后再拆成BBB中的某些數

可以把這樣的操作看成合并某些AAA中的數和某些BBB中的數,使之和相等即可

顯然,最壞的操作上界是A,BA,BA,B都合成只剩一個數,n?1+m?1=n+m?2n-1+m-1=n+m-2n?1+m?1=n+m?2次操作

但是每當有一段AAA的合成數和BBB的一段合成數相同時,A,BA,BA,B就可以不再跟其它的數合并

A,BA,BA,B的操作數都減少111,一共減少了222次總操作數

也就是說,一段和相同,就會減少222次操作,而我們則想要和相同的段數越多越好

可以把BBB中的數取反合在AAA后面,和相同也就變成了求和等于000

fi:f_i:fi?: 操作了iii集合的數的最多段數,sumi:sum_i:sumi?: 選的iii集合的數的和,如果為000,那么fif_ifi?+1+1+1

最后的f(1<<(n+m)?1)f_{(1<<(n+m)-1)}f(1<<(n+m)?1)?肯定會+1+1+1,但是這個多余貢獻是不會減少總操作數的

code

#include <cstdio> #include <iostream> using namespace std; int T, n, m; int A[25], f[1 << 20], sum[1 << 20];int lowbit( int x ) {return x & ( -x ); }int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d", &A[i] );scanf( "%d", &m );for( int i = 1;i <= m;i ++ )scanf( "%d", &A[i + n] ), A[i + n] = - A[i + n];n += m, m = 1 << n, f[0] = 0;for( int i = 1;i < m;i ++ ) {sum[i] = 0, f[i] = 0;for( int j = 1;j <= n;j ++ )if( 1 << j - 1 & i ) {sum[i] = sum[i ^ ( 1 << j - 1 )] + A[j];f[i] = max( f[i ^ ( 1 << j - 1 )] + ( sum[i] == 0 ), f[i] );}}printf( "%d\n", n - 2 - ( f[m - 1] - 1 << 1 ) );}return 0; }

異或

description

N 個數字,要求選擇 M 次,每次從 N 個數中選出兩個數(Ai,Aj)(但不能和之前某次選擇 相同)

此次選擇的得分為 Ai xor Aj。 求最大得分。

Input

第一行包含兩個整數 N,M 接下來一行共 N 個整數描述 N 個數字。

Output

輸出一個整數,表示最大得分除以 10^9+7 的余數

Example

Standard input

3 2

1 2 3

Standard output

5

Note

對于 100%的測試數據,N≤50000,0≤Ai≤10^9

solution

  • 二分第MMM大的值xxx,check異或和大于等于xxx的個數

    • 建字典樹,每個點記錄經過其的個數cntcntcnt

    • 對每個aia_iai?進行字典樹查找,顯然想要大于等于xxx就盡可能異或出現111

      aia_iai?jjj位為1(0)1(0)1(0),那么就去字典樹nownownow0(1)0(1)0(1)端的點找

      • 如果存在,必定會出現1<<j1<<j1<<j的貢獻,加上之前路徑上的貢獻(記為sumsumsum)
        • 如果已經大于等于xxx,則直接加上該點的cntcntcnt,不用繼續往下搜了
        • 如果還不夠,sumsumsum累加該位貢獻,繼續下一位的搜索
      • 如果不存在,那么就只能放棄jjj位的貢獻
        • 如果現在的sumsumsum加上假設后面位全為111的貢獻和(1<<j)?1(1<<j)-1(1<<j)?1都不夠,也不用搜了(不可能)
        • 否則,還是繼續下一位的搜索
    • 為了卡常不TTT,字典樹應只建一次在二分外面,那么就會造成統計大于等于xxx的異或個數時(x,y),(y,x)(x,y),(y,x)(x,y),(y,x)都被計算進入,所以真正的個數應該是要除以222

  • 然后我們就確定了第MMM大的值xxx

    大于等于xxx的個數可能多于MMM個,最后的答案會涉及減去多余個數乘xxx的貢獻

    多余個數的值一定是xxx,不然第MMM大就不可能是xxx

    MMM大是xxx,意味著大于等于x+1x+1x+1的異或組合小于MMM

  • 最后就只剩下求值的問題了

    • 枚舉aia_iai?,邊重構字典樹邊計算答案值和真正的個數,這樣就不需要除以222

    • 走法與上面同理

    • 怎么在計算cntcntcnt的同時計算ansansans,在確定不用繼續往下搜的情況那里統計即可

      字典樹重構時,開一個數組onei:one_i:onei?: 經過該點的第iii位為111的個數

      統計時,枚舉位jjj,如果位jjj要產生1<<j1<<j1<<j的貢獻,顯然數要與aia_iai?jjj位不同才行

      • aia_iai?jjj位為111,則應該為cnt?onejcnt-one_jcnt?onej?的個數
      • aia_iai?jjj位為000,則就是onejone_jonej?的個數

code

#include <cstdio> #include <iostream> using namespace std; #define mod 1000000007 #define maxn 50005 struct node {int cnt;int son[2], one[30]; }t[maxn * 29]; int n, m, tot, mid; long long cnt, ans; int digit[30], a[maxn];void read( int &x ) {x = 0; char s = getchar();while( s < '0' || s > '9' ) s = getchar();while( '0' <= s && s <= '9' ) {x = ( x << 1 ) + ( x << 3 ) + ( s ^ 48 );s = getchar();} }long long dfs_cnt( int now, int d, int sum, int x ) {if( d < 0 ) return 0;long long num = 0;int k = x >> d & 1;if( t[now].son[k ^ 1] ) {if( sum + ( 1 << d ) >= mid ) num += t[t[now].son[k ^ 1]].cnt;else num += dfs_cnt( t[now].son[k ^ 1], d - 1, sum + ( 1 << d ), x );}if( t[now].son[k] && sum + ( 1 << d ) - 1 >= mid ) num += dfs_cnt( t[now].son[k], d - 1, sum, x );return num; }long long calc() {long long num = 0;for( int i = 1;i <= n;i ++ )num += dfs_cnt( 0, 29, 0, a[i] );return ( num >> 1 ); }void add( int now, int x ) {for( int i = 29;~ i;i -- ) {int k = ( x >> i & 1 ) ^ 1;if( k ) ans = ( ans + ( 1ll << i ) * t[now].one[i] ) % mod;else ans = ( ans + ( 1ll << i ) * ( t[now].cnt - t[now].one[i] ) ) % mod;} }long long dfs_sum( int now, int d, int sum, int x ) {if( d < 0 ) return 0;long long num = 0;int k = x >> d & 1;if( t[now].son[k ^ 1] ) {if( sum + ( 1 << d ) >= mid ) add( t[now].son[k ^ 1], x ), num += t[t[now].son[k ^ 1]].cnt;else num += dfs_sum( t[now].son[k ^ 1], d - 1, sum + ( 1 << d ), x );}if( t[now].son[k] && sum + ( 1 << d ) - 1 >= mid ) num += dfs_sum( t[now].son[k], d - 1, sum, x );return num; }void solve() {for( int i = 0;i <= tot;i ++ )t[i].cnt = t[i].son[0] = t[i].son[1] = 0;tot = 0;for( int i = 1;i <= n;i ++ ) {int len = 0;for( int j = 29;~ j;j -- )if( a[i] >> j & 1 ) digit[++ len] = j;int now = 0;for( int j = 29;~ j;j -- ) {int c = a[i] >> j & 1;if( ! t[now].son[c] ) t[now].son[c] = ++ tot;now = t[now].son[c];t[now].cnt ++;for( int k = 1;k <= len;k ++ )t[now].one[digit[k]] ++;}cnt += dfs_sum( 0, 29, 0, a[i] );} }int main() {read( n ), read( m );for( int i = 1;i <= n;i ++ ) {read( a[i] );int now = 0;for( int j = 29;~ j;j -- ) {int c = a[i] >> j & 1;if( ! t[now].son[c] ) t[now].son[c] = ++ tot;now = t[now].son[c];t[now].cnt ++;}}int l = 1, r = ( 1 << 30 ) - 1, val = 1;while( l <= r ) {mid = ( l + r ) >> 1;if( calc() >= m ) val = mid, l = mid + 1;else r = mid - 1;}mid = val;solve();printf( "%lld\n", ( ans - 1ll * val * max( 0ll, cnt - m ) % mod + mod ) % mod );return 0; }

等級

description

數組A中有n個數,下標從1開始。

如果數組中某一段滿足其中的所有元素可以組成一個公差為k的等差數 列,則這一段具備k的等級。

需要注意的是,一個數可以算作公差為任意整數的等差數列。

現在有m個操作,分為兩類:

1 X Y: 表示X位置的數變成Y

2 L R K: 表示查詢[L,R]是否 等級為k。

Input

第一行兩個正整數n,m。 第二行n個非負整數Ai。 接下來m行每一行表示一個操作,格式上文已給出。

強制在線:在本題中x,y,l,r都是經過加密的,需要異或你之前輸出的Yes的個數來進行解密。

由于輸入量較大,建議使用快讀。

Output

對于每個操作2,輸出一行,”Yes”表示可以達到等級K,”No”表示不可以。

Example

standard input

5 3

1 3 2 5 6

2 1 5 1

1 5 4

2 1 5 1

standard output

No

Yes

Note

對于100%的數據,1 ≤ n,m ≤ 300000,0 ≤ Ai,y ≤ 10^9,1 ≤ l ≤ r ≤ n,1 ≤ x ≤ n,0 ≤ k ≤ 10^9

solution

法一:長達7k7k7k的無腦碼農題

  • 線段樹維護區間最大值
  • 線段樹維護區間最小值
  • 線段樹維護區間是否重復——set/map離散化
  • 線段樹維護區間差的gcdgcdgcd
  • 如果是等差數列公差為kkk,必然滿足
    • 最大值?-?最小值=(r?l)?k=(r-l)*k=(r?l)?k
    • 區間無重復
    • gcd=kgcd=kgcd=k

法二:數學真是太強了!

既然是等差數列,則必定滿足等差數列的一次方和,二次方和.........高次方和的多個公式

顯然可以線段樹維護區間的一次方和,二次方和.........

判斷是否跟公式一一符合即可

至于能否被卡,顯然是次方維護越多就越難被卡掉

竊以為到三次方就唯一確定是否是等差數列了

不過本題只用維護到平方和即可

具體公式推導可詳見博主置頂數學公式證明博文,Thanks?(・ω・)ノ

code

#include <cstdio> #include <iostream> using namespace std; #define inf 0x7f7f7f7f #define int long long #define maxn 300005 struct node {int Min, Max, sum1, sum2; }t[maxn << 2]; int n, m; int a[maxn];void read( int &x ) {x = 0; char s = getchar();while( s < '0' || s > '9' ) s = getchar();while( '0' <= s && s <= '9' ) {x = ( x << 1 ) + ( x << 3 ) + ( s ^ 48 );s = getchar();} }void merge( node &t, node x, node y ) {t.Max = max( x.Max, y.Max );t.Min = min( x.Min, y.Min );t.sum1 = x.sum1 + y.sum1;t.sum2 = x.sum2 + y.sum2; }void build( int num, int l, int r ) {if( l == r ) {t[num].Max = t[num].Min = t[num].sum1 = a[l];t[num].sum2 = a[l] * a[l];return;}int mid = ( l + r ) >> 1;build( num << 1, l, mid );build( num << 1 | 1, mid + 1, r );merge( t[num], t[num << 1], t[num << 1 | 1] ); }void modify( int num, int l, int r, int pos ) {if( l == r ) {t[num].Max = t[num].Min = t[num].sum1 = a[l];t[num].sum2 = a[l] * a[l];return;}int mid = ( l + r ) >> 1;if( pos <= mid ) modify( num << 1, l, mid, pos );else modify( num << 1 | 1, mid + 1, r, pos );merge( t[num], t[num << 1], t[num << 1 | 1] ); }node query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) return t[num];int mid = ( l + r ) >> 1;node ans = { inf, -inf, 0, 0 };if( L <= mid ) merge( ans, ans, query( num << 1, l, mid, L, R ) );if( mid < R ) merge( ans, ans, query( num << 1 | 1, mid + 1, r, L, R ) );return ans; }signed main() {scanf( "%lld %lld", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%lld", &a[i] );build( 1, 1, n );int opt, x, y, l, r, k, cnt = 0;while( m -- ) {scanf( "%lld", &opt );if( opt & 1 ) {scanf( "%lld %lld", &x, &y );x ^= cnt, y ^= cnt;if( a[x] == y ) continue;else a[x] = y; modify( 1, 1, n, x );}else {scanf( "%lld %lld %lld", &l, &r, &k );l ^= cnt, r ^= cnt;node MS = query( 1, 1, n, l, r );if( MS.Max - MS.Min != ( r - l ) * k ) {printf( "No\n" );continue;}int N = r - l + 1, a = MS.Min;if( N * a + N * ( N - 1 ) / 2 * k != MS.sum1 ) {printf( "No\n" );continue;}if( N * a * a + N * ( N - 1 ) * k * a + ( N - 1 ) * N * ( 2 * N - 1 ) / 6 * k * k != MS.sum2 ) {printf( "No\n" );continue;}cnt ++;printf( "Yes\n" );}}return 0; }

矩陣

description

有一個n?nn*nn?n的01矩陣,當它每一行、每一列都恰好有兩個位置是1的時候,稱為配對矩陣

請問從1?11*11?1n?nn*nn?n的所有01矩陣中有多少矩陣為配對矩陣。

設 f[i]表示邊長為i的配對矩陣的個數,即詢問:∑i=1nfi\sum_{i=1}^nf_ii=1n?fi?

Input

第一行一個正整數n。

Output

一行一個整數表示所求答案模上998244353的值。

Example

standard input

2

standard output

1

樣例解釋:唯一的矩陣是 n=2,所有位置都是1的矩陣。

Note

對于100%的數據,1 ≤ n ≤ 10^7

solution

法一:王者打表前七個,找規律猜出DPDPDP轉移方程

法二:正常人的推理

轉換題意為:

  • 1×n1\times n1×n的序列 →\rightarrow 一共有nnn
  • 一共nnn次操作 →\rightarrow nnn
  • iii次選兩個格子進行操作+1→+1\rightarrow+1iii行選擇兩個111的列位置
  • 每一列111的位置 →\rightarrow 兩次順序操作i,ji,ji,j選擇了該位置
  • 最后使得每個格子的數都恰好是222

也就是說,如果第iii次操作選擇了j,kj,kj,k意味著第iii行的j,kj,kj,k列為111,第j,kj,kj,k列的其中有個111是在iii

fif_ifi?定義不變,gi:g_i:gi?: 長度序列為iii已經進行了一次操作(已經有兩個位置為111,剩下i?1i-1i?1次操作)的方案數

  • fif_ifi?的轉移

    • fi?2×(i?1)×Ci2f_{i-2}\times(i-1)\times C_i^2fi?2?×(i?1)×Ci2?,兩次操作都選擇同樣的兩個格子

      • 那么兩次操作順序不會造成影響,是等價的,所以是在iii次操作中任選兩次操作Ci2C_i^2Ci2?
      • 剩下i?2i-2i?2長度的空序列fi?2f_{i-2}fi?2?
      • 考慮新增行的位置(隨便考慮一個位置固定也行)是一定要填的,剩下的有Ci?1i=(i?1)C_{i-1}^i=(i-1)Ci?1i?=(i?1)種選擇

    • gi?1×Ci?12×Ai2g_{i-1}\times C_{i-1}^2\times A_{i}^2gi?1?×Ci?12?×Ai2?,兩次操作格子不同

      • 剩下i?1i-1i?1長度序列且有兩個已經為111gi?1g_{i-1}gi?1?

      • 與固定新增塊相連的兩個塊隨便選,Ci?12C_{i-1}^2Ci?12?

      • iii次操作中選兩次操作,操作順序不同,是不等價的,Ai2A_{i}^2Ai2?

  • gig_igi?的轉移

    • fi?2×(i?1)f_{i-2}\times (i-1)fi?2?×(i?1),直接進行一次兩個111位置的操作

      • 剩下i?2i-2i?2長度的空序列,fi?2f_{i-2}fi?2?

      • (i?1)(i-1)(i?1)次操作中,進行一次操作,Ci?11=i?1C_{i-1}^1=i-1Ci?11?=i?1

    • fi?3×(i?2)×Ai?12f_{i-3}\times (i-2)\times A_{i-1}^2fi?3?×(i?2)×Ai?12?,兩個操作額外選的位置相同

      • 剩下i?3i-3i?3長度的空序列,fi?3f_{i-3}fi?3?

      • 額外配對位置選擇有(i?2)(i-2)(i?2)

      • i?1i-1i?1次操作中,兩個操作的順序不同結果不同,Ai?22A_{i-2}^2Ai?22?

    • gi?2×Ai?22×Ai?12g_{i-2}\times A_{i-2}^2\times A_{i-1}^2gi?2?×Ai?22?×Ai?12?,各自選的格子互不相同

      • 剩下i?2i-2i?2長度且已有兩個格子為111的序列,gi?2g_{i-2}gi?2?

      • 兩個格子選擇不同結果不同,與原兩個格子的配對順序也不同,Ai?22A_{i-2}^2Ai?22?

      • i?2i-2i?2次操作中,兩個操作的順序不同結果不同,Ai?22A_{i-2}^2Ai?22?

code

#include <cstdio> #define int long long #define maxn 10000005 #define mod 998244353 int f[maxn], g[maxn]; int n, ans;int C( int x ) {return x * ( x - 1 ) / 2 % mod; }int A( int x ) {return x * ( x - 1 ) % mod; }signed main() {scanf( "%lld", &n );f[0] = f[2] = g[2] = ans = 1;for( int i = 3;i <= n;i ++ ) {f[i] = ( f[i] + f[i - 2] * ( i - 1 ) % mod * C( i ) ) % mod;f[i] = ( f[i] + g[i - 1] * C( i - 1 ) % mod * A( i ) ) % mod; g[i] = ( g[i] + f[i - 2] * ( i - 1 ) ) % mod;g[i] = ( g[i] + f[i - 3] * ( i - 2 ) % mod * A( i - 1 ) ) % mod;g[i] = ( g[i] + g[i - 2] * A( i - 2 ) % mod * A( i - 1 ) ) % mod;ans = ( ans + f[i] ) % mod;}printf( "%lld\n", ans );return 0; }

總結

以上是生活随笔為你收集整理的[2021-07-19 内测NOIP] 操作(状压DP),异或(字典树),等级(线段树),矩阵(DP)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。