AGC 034 F RNG and XOR

发布于 # algorithm

题目连接: https://atcoder.jp/contests/agc034/tasks/agc034_f

道理我都懂,可是不看题解不到啊……

\newcommand \xor{\mathbin{\mathbf{xor}}}

1 题意

给定一个数字 nn

对于 02n10 \cdots 2^n - 1 中每个数字都有一个 aia_i

现在有一个数 XX, 一开始为 00

每一次操作会随机选择一个数字 ii (其中 0i2n10 \leq i \leq 2^n - 1,选中 ii 的概率为 aia\frac{a_i}{\sum a}

然后令 X=XiX = X \oplus i

问使 X=iX=i 的期望次数

2 思路

先令 pi=aiap_i=\frac{a_i}{\sum a}

fif_i 表示从 ii00 的期望次数,这和从 00ii 显然是一样的

接着有一个非常显然的式子

fi=fi\xorj×pj+1(i0)fi1=fi\xorj×pj(i0)\begin{aligned} f_i &= f_{i \xor j} \times p_j + 1 ( i \neq 0 )\\ f_i - 1 &= f_{i \xor j } \times p_j ( i \neq 0 ) \end{aligned}

然后我们就可以得到这个式子

(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ \= ( x, f_1 - 1, f_2 - 1, \cdots, f_{2^{n-1}} - 1, f_{2^n-1} - 1 )

但是 xx 是未知的

注意到 p=1\sum p = 1 所以左边右边的和是相等的

所以

x=i=02n1fii=12n1fi1=f0+2n1\begin{aligned} x &= \sum_{i=0}^{2^n-1} f_i - \sum_{i=1}^{2^n-1} f_i - 1 \\ &= f_0 + 2^n - 1 \end{aligned} (f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ \= ( f_0 + 2^n - 1, f_1 - 1, f_2 - 1, \cdots, f_{2^{n-1}} - 1, f_{2^n-1} -1 )

我们需要的使其中两个式子没有未知数

注意到如果使 p0=p01p_0 = p_0 - 1

那么右边每一个数都会减去 fif_i

(f0,f1,f2,,f2n1,f2n1)(p01,p1,p2,,p2n1,p2n)=(2n1,1,1,,1,1)(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \oplus (p_0 - 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ = ( 2^n - 1, - 1, - 1, \cdots, -1, -1 )

于是就可以放进 FWT 里直接做

然后你就发现这东西跑出来是错的……

为什么呢

P=(p01,p1,p2,,p2n1,p2n)A=(2n1,1,1,,1,1)\begin{aligned} \mathbf{P} & = (p_0 - 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ \mathbf{A} & = ( 2^n - 1, - 1, - 1, \cdots, -1, -1 ) \end{aligned}

因为 P\mathbf{P}A\mathbf{A} FWT 后第一位都是 00

没有办法倒推出来 f0f_0

但是可以发现 ff 的值是可以平移的

fi+k=i=02n1pj(fij+k)+1=i=02n1fij×pj+k+1f_i + k = \sum_{i=0}^{2^n-1} p_j(f_{i \oplus j} + k) + 1 = \sum_{i=0}^{2^n-1} f_{i \oplus j} \times p_j + k + 1

那么我们又知道 fi=0f_i = 0

所以将 ff 每一位都减去 f0f_0 即可

3 Code

#include <cstdio>

const int N = 1 << 20;
const int mod = 998244353;

int n;
int a[N], b[N], p[N], sum;

inline i
nt add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline void add_eq( int &a, int b ) { a = ( a + b ) % mod; }
inline void mul_eq( int &a, int b ) { a = ( 1LL * a * b ) % mod; }

int ksm( int a, int p ) {
    int res = 1;
    while( p ) {
        if( p & 1 )
            res = mul( res, a );
        a = mul( a, a );
        p >>= 1;
    }
    return res;
}
inline int inv( int a ) { return ksm( a, mod - 2 ); }

void XOR( int *f, int len, int x = 1 ) {
    for( int o = 2, k = 1; o <= len; o <<= 1, k <<= 1 ) {
        for( int i = 0; i < len; i += o ) {
            for( int j = 0; j < k; j ++ ) {
                add_eq( f[ i + j ], f[ i + j + k ] );
                f[ i + j + k ] = add( f[ i + j ], add( - f[ i + j + k ], - f[ i + j + k ] ) );
                mul_eq( f[ i + j ], x ); mul_eq( f[ i + j + k ], x );
            }
        }
    }
}

int main() {
#ifdef woshiluo
    freopen( "F.in", "r", stdin );
    freopen( "F.out", "w", stdout );
#endif
    scanf( "%d", &n );
    n = 1 << n;
    for( int i = 0; i < n; i ++ ) {
        scanf( "%d", &a[i] );
        sum += a[i];
        b[i] = mod - 1;
    }
    sum = inv(sum);
    for( int i = 0; i < n; i ++ ) {
        p[i] = mul( a[i], sum );
    }

    b[0] = n - 1; p[0] -= 1;
    XOR( b, n ); XOR( p, n );
    for( int i = 0; i < n; i ++ ){
        mul_eq( b[i], inv( p[i] ) );
    }
    XOR( b, n, inv(2) );
    for( int i = 0; i < n; i ++ ) {
        printf( "%d\n", ( ( add( b[i], -b[0] ) + mod ) % mod ) );
    }
}

将 Bilibili 缓存转换成视频

发布于 # share

0 环境 & 提示

  • 电脑
    • PHP
    • Bash
  • 手机
    • Bilibili 国内版 5.54.0

这样子转换出来还是有水印的,请不要在未经 UP 主同意的情况下传播或用在非法行为

1 过程

因为要备份手机,就想着能不能把 Bilbili 的缓存转换成普通视频留在电脑上

然后看了一下下载目录,里面有一个 video.m4saudio.m4s

mpv 一打开,发现刚好一个是视频一个是音频...

剩下的事情就是拿 ffmpeg 合并一下的事情...

随便胡个脚本就行了

2 脚本

covert.sh
# Author: Woshiluo<[email protected]>
#!/bin/bash

work_dir=`pwd`;
fa_php=`pwd`"/fa.php";
part_php=`pwd`"/part.php";

echo $part_php;
mkdir -p "$work_dir/output"

function covert() {
	cp "$fa_php" ./
	cp "$part_php" ./
	fa=`php fa.php`
	part=`php part.php`
	echo "[INFO] Coverting $fa $part..."
	rm fa.php part.php
	mkdir -p "$work_dir/output/$fa"
	for file in `ls`; do
		if [ -d $file ]; then
			cd $file
			ffmpeg -i video.m4s -i audio.m4s -c copy "$work_dir/output/$fa/$part.mkv" -y >/dev/null 2>&1
			cd ..
		fi
	done
	echo "[INFO] Done!"
}

for file in `ls`; do
	if [ -d $file ] && [ $file != "output" ]; then
		cd ./$file
		for video in `ls`; do
			if [ -d $video ]; then
				cd ./$video
				covert
				cd ..
			fi
		done
		cd ..
	fi
done
fa.php
<?php

$_file = file_get_contents( "./entry.json" );

$desc = json_decode( $_file );

echo $desc -> title;
part.php
<?php

$_file = file_get_contents( "./entry.json" );

$desc = json_decode( $_file );

echo $desc -> page_data -> part;

随便胡的脚本

把这三个文件放到 Bilibili 的下载目录下,然后执行 covert.sh 就行了

执行过后转换过的文件会在 output 目录下

Atcoder ARC 103 F Distance Sums

发布于 # algorithm

0 写在之前

这几天写的题目并不算少

然而这道题是唯一一个看过去傻了的题目...

应该以前听过,题面看着眼熟

可是死活不会做...

1 思路

基本上可以明确是到构造题

这里依赖两个性质:

树的中心的 DD 值是最小的

D(fa) = D(x) - n + 2 \times size\[i\]

这个就是标准的树和树的重心的性质

可以推出来如果以重心做根,那么 DD 越大越在下面

所以尝试生成一颗以重心为根的树就行了

然后我发现我不会判否

结果发现最后生成出来树判断一次就行了...

2 实现

读入 DD 排序

从大到小依此根据上面的式子找父亲

最后判断一下是否正确就可以了

3 Code

// User: woshiluo
// Email: [email protected]
// Problem link: https://atcoder.jp/contests/arc103/tasks/arc103_d
// Comment:
// Why the problem id is 'F', but the link is 'arc103_d'
// Interesting

#include <cstdio>
#include <cstdlib>

#include <map>
#include <algorithm>

const int N = 1e5 + 1e3;

int n;
int size[N], father[N];

struct node{
	int id;
	long long d;
} a[N];

std::map<long long, int> mp;

void wrong() {
	printf( "-1\n" );
	exit(0);
}
bool cmp( node _a, node _b ) { return _a.d < _b.d; }

int main() {
#ifdef woshiluo
	freopen( "F.in", "r", stdin );
	freopen( "F.out", "w", stdout );
#endif
	scanf( "%d", &n );
	for( int i = 1; i <= n; i ++ ) {
		scanf( "%lld", &a[i].d );
		a[i].id = i;
		size[i] = 1;
		mp[ a[i].d ] = i;
	}
	std::sort( a + 1, a + n + 1, cmp );
	int rt_d, rt;
	rt = a[1].id; rt_d = a[1].d;
	for( int i = n; i > 1; i -- ) {
		int fa = mp[ a[i].d + 2LL * size[ a[i].id ] - n ];
		if( fa == 0 ) {
			wrong();
			return 0;
		}
		size[fa] += size[ a[i].id ];
		father[ a[i].id ] = fa;
	}
	for( int i = 1; i <= n; i ++ ) {
		if( i == rt )
			continue;
		 rt_d -= size[i];
	}
	if( rt_d != 0 )
		wrong();

	for( int i = 1; i <= n; i ++ ) {
		if( i == rt )
			continue;
		printf( "%d %d\n", father[i], i );
	}
}

HDU 4507 恨7不成妻

发布于 # algorithm

思路

裸的要死的数位 DP 题目

一眼看过去,平方和,有点懵

但是维护平方和也是老套路了

维护 sumsum, cntcnt, powsumpow_sum 就可以了

代码

代码写的着急,会有一些不太好看

#include <cstdio>

const int mod = 1e9 + 7;

inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline int power( int a ) { return mul( a, a ); }

int T, max_len;
long long left, rig;
int _count[110][2][10][10][10], _sum[110][2][10][10][10], _pow[110][2][10][10][10];
int max_count[110][2][10][10][10], max_sum[110][2][10][10][10], max_pow[110][2][10][10][10];
bool _vis[110][2][10][10][10];
int a[110];

inline int length( int cur, int len ) {
	int res = cur;
	while( len > 0 ) {
		res = mul( 10, res );
		len --;
	}
	return res;
}

struct node {
	int len, cur, seven, rem, base_rem;
	bool max;
	int& count() {
		if( max )
			return max_count[len][seven][rem][cur][base_rem];
		return _count[len][seven][rem][cur][base_rem];
	}
	int& sum() {
		if( max )
			return max_sum[len][seven][rem][cur][base_rem];
		return _sum[len][seven][rem][cur][base_rem];
	}
	int& pow() {
		if( max )
			return max_pow[len][seven][rem][cur][base_rem];
		return _pow[len][seven][rem][cur][base_rem];
	}
	bool& vis() {
		return _vis[len][seven][rem][cur][base_rem];
	}
	void print() {
//		printf( "%d %d %d %d %d %d\n", len, cur, seven, rem, base_rem, max );
//		printf( "%d %d %d\n", count(), sum(), pow() );
	}
};

int dfs( node cur ) {
	if( ( ! cur.max ) && cur.vis() )
		return cur.pow();
	if( cur.len == 0 ) {
		if( cur.seven || cur.rem == 0 || cur.base_rem == 0 ) {
			cur.count() = cur.sum() = cur.pow() = 0;
			return cur.pow();
		}
		cur.count() = 1; cur.sum() = cur.cur; cur.pow() = power( cur.cur );
		return cur.pow();
	}
	int &cur_count = cur.count();
	int &cur_sum = cur.sum();
	int &cur_pow = cur.pow();
	if( cur.max )
		cur_count = cur_sum = cur_pow = 0;
	for( int i = 0; i <= ( cur.max? a[cur.len]: 9 ); i ++ ) {
		node nxt = cur;
		nxt.len -= 1; nxt.seven = ( cur.seven || ( i == 7 ) ); nxt.cur = i; nxt.max = ( cur.max && i == a[cur.len] );
		nxt.rem = ( cur.rem * 10 + i ) % 7; nxt.base_rem = ( cur.base_rem + i ) % 7;
		dfs( nxt );
		cur_count = add( cur_count, nxt.count() );
		cur_sum = add( cur_sum, add( mul( length( cur.cur, cur.len ), nxt.count() ), nxt.sum() ) );
		cur_pow = add( cur_pow, add( nxt.pow(), add( mul( nxt.count(), power( length( cur.cur, cur.len ) ) ),
						mul( mul( 2, length( cur.cur, cur.len ) ), nxt.sum() ) ) ) );
	}
	if( cur.max == false )
		cur.vis() = true;
	cur.print();
	return cur_pow;
}

int sum( long long _a ) {
	if( _a == 0 )
		return 0;
	if( _a < 0 )
		return 0;
	max_len = 0;
	while( _a ) {
		a[ ++ max_len ] = _a % 10;
		_a /= 10;
	}
//	printf( "ans: %d\n", dfs( (node){ max_len, 0, 0, 0, 0, true } ) );
	return dfs( (node){ max_len, 0, 0, 0, 0, true } );
}

int main() {
#ifdef woshiluo
	freopen( "hdu.4507.in", "r", stdin );
	freopen( "hdu.4507.out", "w", stdout );
#endif
	scanf( "%d", &T );
	while( T -- ) {
		scanf( "%lld%lld", &left, &rig );
		if( left == 0 )
			printf( "%d\n", ( sum(rig) + mod ) % mod );
		else
			printf( "%d\n", ( add( sum(rig), - sum( left - 1 ) ) + mod ) % mod );
	}
}

Luogu P3177 [HAOI 2015] 树上染色

发布于 # algorithm

题目链接: https://www.luogu.com.cn/problem/P3177

0 说在之前

树形 DP 要么模板题,要么神仙题

这个题就属于不太正常的

1 思路

fi,jf_{i,j}

其中 ii 代表当前节点

jj 代表子树中选择 jj 个黑点,所能对答案产生的贡献

有了这个思路,后面的东西就是标准的树上背包了

2 Code

#include <cstdio>
#include <cstring>

inline int Min(int a, int b) { return a < b? a : b; }
inline long long Max(long long a, long long b) { return a > b? a : b; }

const int N = 2100;

int n, m;
int size[N];

// Edge Start
struct edge {
    int to, val, next;
} e[N << 1];
int ehead[N], ecnt;

inline void add_edge(int now, int to, int val) {
    ecnt ++;
    e[ecnt].to = to;
    e[ecnt].val = val;
    e[ecnt].next = ehead[now];
    ehead[now] = ecnt;
}
// Edge End

// DP Start
long long f[N][N];
void dp(int now, int la) {
    size[now] = 1;
    f[now][0] = f[now][1] = 0;
    for(int i = ehead[now]; i; i = e[i].next) {
        if( e[i].to == la )
            continue;
        dp(e[i].to, now);
        size[now] += size[ e[i].to ];
        for(int j = Min(size[now], m); j >= 0; j--) {
            for(int k = 0; k <= Min(size[ e[i].to ], j); k++) {
                if( f[now][ j - k ] == -1 )
                    continue;
                long long val = 1LL * ( 1LL * k * ( m - k ) + 1LL * ( size[ e[i].to ] - k ) * ( n - m - size[ e[i].to ] + k ) ) * e[i].val;
                f[now][j] = Max( f[now][j], f[now][j - k] + f[ e[i].to ][k] + val);
            }
        }
    }
}
// DP End

int main() {
#ifdef woshiluo
    freopen("luogu.3177.in", "r", stdin);
    freopen("luogu.3177.out", "w", stdout);
#endif
    scanf("%d%d", &n, &m);
    for(int i = 1, u, v, w; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w);
        add_edge(v, u, w);
    }

    memset(f, -1, sizeof(f));
    dp(1, 0);

    printf("%lld", f[1][m]);
    fclose(stdin);
    fclose(stdout);
}

Woshiluo's NoteBook

「Jump up HIGH!!」