[2020-11-30 contest]數列(矩陣加速),祕密通道(dijkstra最短路)小X遊世界樹(換根dp),劃分(數學)
數列
a[1]=a[2]=a[3]=1 a[x]=a[x-3]+a[x-1] (x>3) 求 a 數列的第 n 項對 1000000007(10^9+7)取餘的值。
輸入格式
第一行一個整數 T,表示詢問個數。 以下 T 行,每行一個正整數 n。
輸出格式
每行輸出一個非負整數表示答案。
樣例
輸入樣例
3
6
8
10
輸出樣例
4
9
19
資料範圍與提示
對於 30%的資料 n<=100; 對於 60%的資料 n<=2x107
對於 100%的資料 T<=100,n<=2x109
solution
一眼題,沒得說
[ a 1 , a 2 , a 3 ] × [ 0 0 1 1 0 0 0 1 1 ] = [ a 2 , a 3 , a 4 ] \begin{bmatrix} a_1,a_2,a_3 \end{bmatrix}\times\begin{bmatrix} 0&0&1\\ 1&0&0\\ 0&1&1 \end{bmatrix}=\begin{bmatrix} a_2,a_3,a_4 \end{bmatrix} [a1,a2,a3]×⎣⎡010001101⎦⎤=[a2,a3,a4]
code
#include <cstdio>
#include <cstring>
#define mod 1000000007
#define ll long long
struct Matrix {
ll c[4][4];
Matrix() {
memset( c, 0, sizeof( c ) );
}
Matrix operator * ( const Matrix &p ) {
Matrix ans;
for( int i = 1;i <= 3;i ++ )
for( int j = 1;j <= 3;j ++ )
for( int k = 1;k <= 3;k ++ )
ans.c[i][k] = ( ans.c[i][k] + c[i][j] * p.c[j][k] % mod ) % mod;
return ans;
}
}base, v, result;
int T, n;
Matrix qkpow( Matrix x, int y ) {
Matrix res;
res.c[1][1] = res.c[2][2] = res.c[3][3] = 1;
while( y ) {
if( y & 1 ) res = res * x;
x = x * x;
y >>= 1;
}
return res;
}
int main() {
freopen( "seq.in", "r", stdin );
freopen( "seq.out", "w", stdout );
base.c[1][1] = base.c[1][2] = base.c[1][3] = 1;
v.c[1][3] = v.c[2][1] = v.c[3][2] = v.c[3][3] = 1;
scanf( "%d", &T );
while( T -- ) {
scanf( "%d", &n );
if( n <= 3 ) {
printf( "1\n" );
continue;
}
result = base * qkpow( v, n - 3 );
printf( "%lld\n", result.c[1][3] );
}
return 0;
}
祕密通道
有一副 n × m n\times m n×m的地圖,有 n × m n\times m n×m塊地,每塊是下列四種中的一種: 牆:用#表示,牆有 4 個面,分別是前面,後面,左面,右面。 起點:用 C 表示,為主角的起點,是一片空地。 終點:用 F 表示,為主角的目的地,是一片空地。 空地:用 . 表示。 其中除了牆不能穿過,其他地方都能走。 主角有以下 3 種操作:
移動到相鄰的前後左右的地方,花費一個單位時間。
向前後左右其中一個方向發射子彈,子彈沿直線穿過,打在最近的一堵牆的一面,然 後牆的這面就會形成一個開口通往祕密通道。同一時間最多隻能有兩個開口,若出現有 3 個 開口,出現時間最早的開口會立即消失。該操作不用時間。
可以從一個與開口相鄰的空地跳進去,進入祕密通道,從另外一個開口正對的空地跳 出來。這個過程花費一個單位時間。 地圖四周都是牆,問主角最少用多少時間從 C 走到 F。C 和 F 只會出現一次。
輸入格式
第一行輸入兩個正整數 n,m。 接下來 n 行,每行 m 個字元描述地圖。
輸出格式
輸出 1 個整數,表示最短時間完成路途。如果無解輸出 nemoguce
樣例
輸入樣例1
4 4
#.F#
#C.#
輸出樣例1
2
輸入樣例2
6 8
########
#.##…F#
#C.##…#
#…#…#
#…##
########
輸出樣例2
4
資料範圍與提示
對於 50%的資料,4 ≤ ?, ? ≤ 15。 對於 100%的資料,4 ≤ ?, ? ≤ 500。
solution
兩眼題
這種走迷宮條件反射就想上bfs,不過很明顯⛏最短路
這道題還是很善良的,打槍是沒有時間的,當然就算有也一樣簡單
但是考場上,我想錯了,以為走到牆旁邊的格子再打槍一定是最優的
然並卵?
很明顯,我首先就得往下打一槍,再往右走,打槍穿牆瞬移
於是我被卡了30′
所以,對於地圖上的每一個點都有八條建邊可能
四個方向相鄰格子建邊邊權
1
1
1
四個牆相鄰格子建邊均為四個方向上最近的牆的距離
n
×
m
n\times m
n×m個點,普通
O
(
n
2
)
d
i
j
k
s
t
r
a
O(n^2)dijkstra
O(n2)dijkstra整不動,就上堆優化
code
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 505
struct node {
int i, j, w;
node(){}
node( int I, int J, int W ) {
i = I, j = J, w = W;
}
bool operator < ( const node &u ) const {
return w > u.w;
}
};
priority_queue < node > q;
int n, m;
char s[maxn][maxn];
bool vis[maxn][maxn];
int Up[maxn][maxn], Down[maxn][maxn], Left[maxn][maxn], Right[maxn][maxn];
bool inside( int x, int y ) {
if( x < 1 || x > n || y < 1 || y > m ) return 0;
else return 1;
}
void Push( int x, int y, int w ) {
if( ! inside( x, y ) ) return;
if( s[x][y] == '#' ) return;
if( vis[x][y] ) return;
q.push( node( x, y, w ) );
}
void Dijkstra() {
while( ! q.empty() ) {
node u = q.top(); q.pop();
int i = u.i, j = u.j, w = u.w;
if( vis[i][j] ) continue;
if( s[i][j] == 'F' ) {
printf( "%d\n", w );
return;
}
vis[i][j] = 1;
Push( i - 1, j, w + 1 );
Push( i + 1, j, w + 1 );
Push( i, j - 1, w + 1 );
Push( i, j + 1, w + 1 );
int minn = min( min( i - Up[i][j], Down[i][j] - i ), min( j - Left[i][j], Right[i][j] - j ) );
Push( Up[i][j] + 1, j, w + minn );
Push( Down[i][j] - 1, j, w + minn );
Push( i, Left[i][j] + 1, w + minn );
Push( i, Right[i][j] - 1, w + minn );
}
printf( "nemoguce\n" );
}
int main() {
scanf( "%d %d", &n, &m );
for( int i = 1;i <= n;i ++ ) {
scanf( "%s", s[i] + 1 );
for( int j = 1;j <= m;j ++ )
if( s[i][j] == 'C' ) q.push( node( i, j, 0 ) );
}
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= m;j ++ ) {
if( s[i - 1][j] == '#' ) Up[i][j] = i - 1;
else Up[i][j] = Up[i - 1][j];
if( s[i][j - 1] == '#' ) Left[i][j] = j - 1;
else Left[i][j] = Left[i][j - 1];
}
for( int i = n;i;i -- )
for( int j = m;j;j -- ) {
if( s[i + 1][j] == '#' ) Down[i][j] = i + 1;
else Down[i][j] = Down[i + 1][j];
if( s[i][j + 1] == '#' ) Right[i][j] = j + 1;
else Right[i][j] = Right[i][j + 1];
}
Dijkstra();
return 0;
}
小X遊世界樹
小 x 得到了一個(不可靠的)小道訊息,傳說中的神島阿瓦隆在格陵蘭海的某處,據說那 裡埋藏著亞瑟王的寶藏,這引起了小 x 的好奇,但當他想前往阿瓦隆時發現那裡只有聖誕節 時才能到達,然而現在已經春天了,不甘心的他將自己的目的地改成了世界樹,他耗費了大 量的時間,終於將自己傳送到了世界樹下。世界樹是一棵非常巨大的樹,它有著許許多多的 枝條以及節點,每個節點上都有一個平臺。好不容易來到傳說中的世界樹下,小 x 當然要爬 上去看看風景。小 x 每經過一條邊都會耗費體力值。然而世界樹之主想給他弄(gáo)些 (d ǐan)麻(shì)煩(qíng),於是他在每條邊上都設了一個魔法陣,當小 x 踏上那條邊時會 被傳送回根節點,魔法陣只生效一次。這豈不是要累死小 x?幸運的是,每個平臺上都有無 數個加速器,這些加速器可以讓小 x 在當前節點所連的邊上耗費的體力值減少,不同平臺的 加速器效能不一定相同,但同一個平臺的加速器效能絕對相同。世界樹之主給了小 x 一次 “換根”的機會,他可以將世界樹的任何一個節點變為根,但所有的邊都不能改變。小 x 想 問你,將根換為哪個節點能使小 x 爬到世界樹上的每個節點耗費的體力值和最少。預設編號 為 1 的點為初始根
輸入格式
第一行一個數 n,表示有 n 個節點。 第二行 n 個數 ai,表示每個平臺上的加速器的效能。 第三至 n+1 行,每行三個數 bi,ci,di 分別表示這條無向邊的起點,終點與耗費的能量值。
輸出格式
第一行一個數,表示要換成的節點,如果有多個點為根時耗費的體力值都最小,則輸出 編號最小的那個。如果保持為 1 是最優的,就輸出 1。 第二行一個數,表示最小耗費的體力值。
樣例
輸入樣例
4
2 1 3 3
1 2 3
1 3 4
2 4 6
輸出樣例
1
9
資料範圍與提示
如果以第一個點為根,則需要耗費 0(到 1)+1(到 2)+2(到 3)+6(到 4)=9 的能量值。 如果以第二個點為根,則需要耗費 2(到 1)+0(到 2)+4(到 3)+5(到 4)=11 的能量值。 如果以第三個點為根,則需要耗費 1(到 1)+2(到 2)+0(到 3)+7(到 4)=10 的能量值。 如果以第四個點為根,則需要耗費 5(到 1)+3(到 2)+7(到 3)+0(到 4)=15 的能量值。 很明顯以第一個點為根是最優的。
對於 20%的資料:n<=100 對於 40%的資料:n<=1000 對於 60%的資料:n<=8000 對於 80%的資料:n<=100000 對於 100%的資料:0<n<=700000;ai<=1000;1<=bi,ci<=n;di<=1000。
solution
一眼題,沒得跑,換根dp還是很裸的了
g
[
u
]
g[u]
g[u]:
u
u
u子樹內每個點到
u
u
u所耗費的體力值之和
f
[
u
]
f[u]
f[u]:除
u
u
u子樹外所有點到
u
u
u所耗費的體力值之和
很明顯?
g
[
u
]
g[u]
g[u]與
u
u
u兒子掛鉤,需要從下往上更新
f
[
u
]
f[u]
f[u]與
u
u
u祖先掛鉤,需要從上往下更新
所以兩個分開用
d
f
s
dfs
dfs⛏
然而我因為最後的極大值附小了,於是愉快地又爆掉了30′
code
#include <cstdio>
#include <vector>
using namespace std;
#define maxn 700005
#define ll long long
vector < pair < int, int > > G[maxn];
int n;
int a[maxn], siz[maxn];
ll f[maxn], g[maxn], ans[maxn];
void dfs1( int u, int fa ) {
siz[u] = 1;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( v == fa ) continue;
dfs1( v, u );
siz[u] += siz[v];
g[u] += g[v] + 1ll * siz[v] * ( w - a[u] );
}
}
void dfs2( int u, int fa ) {
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
if( v == fa ) continue;
f[v] = f[u] + g[u] - g[v] - 1ll * siz[v] * ( w - a[u] ) + 1ll * ( n - siz[v] ) * ( w - a[v] );
ans[v] = g[v] + f[v];
dfs2( v, u );
}
}
int main() {
scanf( "%d", &n );
for( int i = 1;i <= n;i ++ )
scanf( "%d", &a[i] );
for( int i = 1, u, v, w;i < n;i ++ ) {
scanf( "%d %d %d", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
G[v].push_back( make_pair( u, w ) );
}
dfs1( 1, 0 );
dfs2( 1, 0 );
ll result = 1ll << 60; int pos;
ans[1] = g[1];
for( int i = 1;i <= n;i ++ )
if( ans[i] < result ) result = ans[i], pos = i;
printf( "%d\n%lld", pos, result );
return 0;
}
劃分
有一個未知的序列 x,長度為 n。它的 K-劃分序列 y 指的是每連續 K 個數的和得到劃 分序列,y[1]=x[1]+x[2]+…+x[K],y[2]=x[K+1]+x[K+2]+…+x[K+K]…。 若 n 不被 K 整除,則 y[n/K+1]可以由少於 K 個數加起來。 比如 n=13,K=5,則 y[1]=x[1]+…+x[5],y[2]=x[6]+…+x[10],y[3]=x[11]+x[12]+ x[13]。若小 A 只確定 x 的 K[1]劃分序列以及 K[2]劃分序列…K[M]劃分序列的值情況下, 問她可以確定 x 多少個元素的值。
輸入格式
第一行輸入兩個正整數 n,M。 第二行輸入 M 個正整數表示 K[1],K[2]…K[M]。
輸出格式
輸出 1 個整數,表示能確定的元素
樣例
輸入樣例1
3 1
2
輸出樣例1
1
輸入樣例2
6 2
2 3
輸出樣例2
2
資料範圍與提示
【樣例解釋】 【樣例 1 解釋】 小 A 知道 x 的 2-劃分序列,即分別知道 x[1]+x[2],x[3]的值。 小 A 可以知道 x[3]的值。 【樣例 2 解釋】 小 A 知道 x 的 2-劃分序列,即分別知道 x[1]+x[2],x[3]+x[4],x[5]+x[6] 的值。 小 A 知道 x 的 3-劃分序列,即分別知道 x[1]+x[2]+x[3] ,x[4]+x[5]+x[6] 的值。 小 A 可以知道 x[3],x[4]的值,個數為 2. 【資料範圍】 對於 20%的資料,3 ≤ ? ≤ 2000, ? ≤ 3。 對於 40%的資料,3 ≤ ? ≤ 5 ∗ 10^6。 對於 100%的資料,3 ≤ ? ≤ 10^9, 1 ≤ ? ≤ 10,2 ≤ ?[?] < ?。
solution
一眼題
一眼做不出來的題
首先很明顯可以發現,若要確定
x
x
x,當且僅當我們知道
S
x
,
S
x
−
1
S_x,S_{x-1}
Sx,Sx−1
即
x
=
k
1
×
a
=
k
2
×
b
+
1
=
>
k
1
×
a
−
k
2
×
b
=
1
x=k_1\times a=k_2\times b+1\ =>\ k_1\times a-k_2\times b=1
x=k1×a=k2×b+1 => k1×a−k2×b=1
發現這個式子長得很像擴充套件歐幾里得
a
x
+
b
y
=
1
ax+by=1
ax+by=1,當且僅當
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1有解
求出最小正整數解
a
1
,
b
1
a_1,b_1
a1,b1
則通解
a
a
a為
a
1
+
p
×
b
1
,
p
∈
Z
a_1+ p\times b_1,p∈Z
a1+p×b1,p∈Z
去解一個不等式
p
×
b
1
×
k
i
+
a
1
∗
b
1
≤
n
p\times b_1\times k_i+a_1*b_1\le n
p×b1×ki+a1∗b1≤n
要是真這麼簡單就歐兒啦
我們不僅可以很明顯的發現有解的條件是相差為
1
1
1,也能很明顯地發現我們會算重
就算發現不了樣例也會告訴你血的教訓
容斥就好了,可我容斥不行啊!!
我們設
s
1
s_1
s1表示所有
a
a
a集合,
s
2
s_2
s2表示所有
b
b
b的方程。
這樣每一個
k
i
k_i
ki只會存在於
s
1
,
s
2
s_1,s_2
s1,s2或者都不存在。
這樣列舉的複雜度是
O
(
3
m
)
O(3^m)
O(3m)的
容斥係數就是
(
−
1
)
∣
A
∣
+
∣
B
∣
(−1)^{|A|+|B|}
(−1)∣A∣+∣B∣ 推一推找找規律就知道了
這裡涉及了同餘方程的合併,就是他們的 l c m lcm lcm
code
#include <cmath>
#include <cstdio>
#define int long long
#define maxm 15
#define maxn 60000
int n, m, ans;
int k[maxm], f[maxn];
int gcd( int x, int y ) {
if( ! y ) return x;
else return gcd( y, x % y );
}
int lcm( int x, int y ) {
int d = gcd( x, y );
return x / d * y;
}
int exgcd( int a, int b, int &x, int &y ) {
if( ! b ) {
x = 1, y = 0;
return a;
}
else {
int d = exgcd( b, a % b, y, x );
y -= x * ( a / b );
return d;
}
}
int calc( int a, int b ) {
int x, y;
if( exgcd( a, b, x, y ) != 1 ) return 0;
x = ( x % b + b ) % b;
if( x * a > n ) return 0;
else return ( n - x * a ) / ( a * b ) + 1;
}
void dfs( int s1, int s2, int x, int t1, int t2 ) {
if( s1 > n || s2 > n ) return;
if( x > m ) {
if( ! t1 || ! t2 ) return;
ans += ( ( t1 + t2 ) & 1 ? -1 : 1 ) * calc( s1, s2 );
return;
}
dfs( lcm( s1, k[x] ), s2, x + 1, t1 + 1, t2 );
dfs( s1, lcm( s2, k[x] ), x + 1, t1, t2 + 1 );
dfs( s1, s2, x + 1, t1, t2 );
}
signed main() {
scanf( "%lld %lld", &n, &m );
for( int i = 1;i <= m;i ++ )
scanf( "%lld", &k[i] );
k[++ m] = n;
dfs( 1, 1, 1, 0, 0 );
printf( "%lld", ans );
return 0;
}
相關文章
- 幸運數(dp+矩陣加速)矩陣
- 巨大的數(dp+矩陣加速)矩陣
- 序列(dp+矩陣加速)矩陣
- 矩陣快速冪加速最短路矩陣
- 動態dp & 矩陣加速遞推矩陣
- 樹上的等差數列 [樹形dp]
- Python變數小祕密Python變數
- BZOJ 3329 Xorequ:數位dp + 矩陣快速冪矩陣
- java大陣列劃分為若干小陣列Java陣列
- L2-001 緊急救援 (25分)【新增陣列求最短路數量】【pre陣列記錄最短路徑】陣列
- 整數劃分(硬幣問題)(dp)
- 換根dp
- 換根 DP
- 斐波那契數列Ⅳ【矩陣乘法】矩陣
- 巨大的矩陣(矩陣加速)矩陣
- 演算法學習:矩陣快速冪/矩陣加速演算法矩陣
- AtCoder Beginner Contest 370 E(計數 + DP)
- 矩陣求最短路徑矩陣
- C語言:將一個4X4的矩陣賦值,矩陣元素的值為其行數與列數的乘積C語言矩陣賦值
- 將字串陣列轉換為浮點數陣列字串陣列
- 陣列元素劃分陣列
- POJ 3233 Matrix Power Series (矩陣快速冪+等比數列二分求和)矩陣
- 列舉(矩陣消除、七段數碼管)矩陣
- 人工智慧數學基礎—-矩陣人工智慧矩陣
- 人工智慧數學基礎----矩陣人工智慧矩陣
- 空夜 [換根DP]
- BZOJ3329: Xorequ(二進位制數位dp 矩陣快速冪)矩陣
- HDU 4549 M斐波那契數列(矩陣快速冪+費馬小定理)矩陣
- 列主序儲存的4x4座標變換矩陣矩陣
- 求區間不同數的個數【樹狀陣列求解】陣列
- systemtap 探祕(三)- 型別、變數和陣列型別變數陣列
- 數位DP小記
- cuda 加速矩陣乘法矩陣
- 【精選】矩陣加速矩陣
- [ABC337G] Tree Inversion(換根 dp + 值域線段樹)
- 矩陣樹定理矩陣
- 高等代數1 矩陣矩陣
- 線性代數--矩陣矩陣