给定 A,B,C A , B , C ,进行以下操作 K K 次:
求 K K 次操作后 A−B A − B 的值,如果答案大于 1018 10 18 ,输出'unfair'
A,B,C≤109,K≤1018 A , B , C ≤ 10 9 , K ≤ 10 18
设每次操作后每个数的值等于 αx+βy α x + β y ,其中 x x 为最开始时自己的值, y y 为最开始时其他两个数的和, 我们要求的就是
αA+βB−αB−βA α A + β B − α B − β A
我们发现对于3个数,每次操作后的 α α 和 β β 都是相等的。
找一找规律,我们发现偶数次操作后, α−β=1 α − β = 1 ,奇数次操作后, β−α=1 β − α = 1
所以我们可以 O(1) O ( 1 ) 求出答案。而且我们发现答案并不会超过 1018 10 18
code:
#include
using namespace std;
int a, b, c; long long k;
int main()
{
scanf( "%d%d%d%lld", &a, &b, &c, &k );
if( k % 2 == 0 )
printf( "%d\n", a - b );
else printf( "%d\n", b - a );
return 0;
}
给定一个序列 P P ,它是 1 1 至 N N 的排列,你要通过做以下操作来将该序列排序成递增的:
求需要的最少的操作次数
N≤2×105 N ≤ 2 × 10 5
可以发现,将 N N 至 1 1 依次放到序列最前方或者将 1 1 至 N N 依次放到序列最后方是一定可以将序列排好序的
这个操作不允许我们将一个数放到两个数中间,如果我们存在 x,x+1,x+2 x , x + 1 , x + 2 ,其中 x+2 x + 2 在 x x 和 x+1 x + 1 中间,那我们就只能将 x+2 x + 2 至 N N 或 1 1 至 x+1 x + 1 暴力重构
所以我们只需要找出序列中最长的连续上升子序列,这部分是不需要重构的,然后将不属于这个子序列的数都暴力重构就可以了
code:
#include
using namespace std;
const int N = 300000;
int n, a[N], f[N], ans = 0;
inline void ck_max( int &a, int b ) { if( a < b ) a = b; }
int main()
{
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ )
scanf( "%d", &a[i] );
memset( f, 0, sizeof( f ) );
for( int i = 1; i <= n; i ++ )
f[ a[i] ] = f[ a[i] - 1 ] + 1, ck_max( ans, f[ a[i] ] );
printf( "%d\n", n - ans );
return 0;
}
有一个长度为 N N 的序列 X X ,其中每个元素都是 0 0
给你一个长度为 N N 的序列 A A ,你可以对 X X 进行任意次以下操作:
输出将 X X 变成 A A 的最少操作数,如果不能的话,输出 −1 − 1
N≤2×105,0≤Ai≤109 N ≤ 2 × 10 5 , 0 ≤ A i ≤ 10 9
如果我们要将序列中某一个数变成 N N 的话,那我们必定会在某一时刻将序列变成以下这种样子
然而我们是没有办法将一个数减少的,所以如果 Ai−Ai−1≥2 A i − A i − 1 ≥ 2 ,那么一定是不可以的(我们设 A0=−1 A 0 = − 1 )
对于序列中的一个数 i i ,如果 Ai−Ai−1=1 A i − A i − 1 = 1 ,我们只需要在做好 Ai−1 A i − 1 的基础上进行一次操作就可以做好 Ai A i ,否则我们需要暴力的进行 Ai A i 次操作
至此我们扫一遍即可解决问题
code:
#include
using namespace std;
const int N = 300000;
int n, a[N]; long long ans = 0;
int main()
{
scanf( "%d", &n );
for( int i = 1; i <= n; i ++ )
scanf( "%d", &a[i] );
if( a[1] != 0 ) {
printf( "-1\n" ); return 0;
}
for( int i = 2; i <= n; i ++ )
{
if( a[i] - a[i-1] >= 2 ) {
printf( "-1\n" ); return 0;
}
if( a[i] - a[i-1] == 1 )
ans ++;
else ans += a[i];
}
printf( "%lld\n", ans );
return 0;
}
对于一颗树 G G 的顶点染色方案,如果同种颜色的每个节点作为根时形成的有根树都是同构的,那我们称这种染色方案为 good coloring,将这种染色方案的颜色总数称为这棵树的 colofulness
给定一颗 N N 个结点的树,你可以进行任意次以下操作来重构它:
找出重构后树的最小 colorfulness,在 colorfulness 最小的情况下,找出最小的叶子数量
N≤200 N ≤ 200
我们发现增加新点并不能使树的 colorfulness 减少
我们找出树的直径,设其长度为 D D ,我们可以发现该树的 colorfulness 为 ⌊D2⌋ ⌊ D 2 ⌋
所以第一个任务就完成了
每棵树都有一个或两个中心,当 D D 为偶数时,中间的两个点就是中心,当 D D 为奇数时,中间的一个点可以是中心,那个点与其相邻的任意一个点也可以组成两个点的中心。
我们发现对于一个确定的中心,这棵树的叶子数量是一定的
我们只需要暴力枚举中心,找出最小的叶子数量即可
code:
(这份代码 WA 了一个点…并不知道原因,求大神解答)
#include
const int N = 200;
const int INF = 1<<30;
inline int read()
{
char c = getchar(); int f = 1, x = 0;
while( c < '0' || c>'9' ) { if(c == '-') f = -1; c = getchar(); }
while( c >= '0'&&c<='9' ) { x = x * 10 + c - '0'; c = getchar(); }
return f * x;
}
int tot = 0, front[N];
struct tEdge {
int v, next;
} e[N<<1];
inline void add_edge( int u, int v ) {
e[++tot] = { v, front[u] }; front[u] = tot;
e[++tot] = { u, front[v] }; front[v] = tot;
}
int n, m = INF, max_dep[N];
inline int find_max_dep( int u, int fa )
{
int ret = 0;
for( int i = front[u]; i; i = e[i].next )
{
int v = e[i].v;
if( v == fa ) continue;
ret = std::max( ret, find_max_dep( v, u ) );
}
return ret + 1;
}
int sum[N];
inline int find_leaves( int u, int fa, int dep )
{
int now_sum = 0;
for( int i = front[u]; i; i = e[i].next )
{
int v = e[i].v;
if( v == fa ) continue;
find_leaves( v, u, dep + 1 );
now_sum ++;
}
sum[dep] = std::max( sum[dep], now_sum );
}
inline int calc_ans( int u, int v )
{
int ret = 0;
if( !v )
{
for( int i = front[u]; i; i = e[i].next )
find_leaves( e[i].v, u, 1 ), ret ++;
for( int i = 1; sum[i]; i ++ )
ret *= sum[i], sum[i] = 0;
return ret == 0 ? 1 : ret;
}
ret = 2;
find_leaves( u, v, 1 );
find_leaves( v, u, 1 );
for( int i = 1; sum[i]; i ++)
ret *= sum[i], sum[i] = 0;
return ret;
}
int main()
{
memset( max_dep, 0, sizeof( max_dep ) );
n = read();
for( int i = 1; i < n; i ++ )
add_edge( read(), read() );
int x = 0, y = 0;
for( int i = 1; i <= n; i ++ )
max_dep[i] = find_max_dep( i, 0 ),
m = std::min( m, max_dep[i] );
for( int i = 1; i <= n; i ++ )
if( max_dep[i] == m )
y = x ? i : 0, x = x ? x : i;
int ans;
if( !y )
{
ans = calc_ans( x, 0 );
for( int i = front[x]; i; i = e[i].next )
ans = std::min( ans, calc_ans( x, e[i].v ) );
}
else ans = calc_ans( x, y );
printf( "%d %d\n", m - ( !!y ), ans );
return 0;
}
找出符合以下条件的序列组 (A0,A1,...,AN) ( A 0 , A 1 , . . . , A N ) 的数量,模 M M
1≤N,K≤300,2≤M≤109 1 ≤ N , K ≤ 300 , 2 ≤ M ≤ 10 9
每个序列 Ai A i 就相当于在上一个序列 Ai−1 A i − 1 的基础上插入了一个数字,由于要求字典序递增,插入的数字必须大于等于原来在该位置上的数字。然而如果相等的话,我们其实就相当于在原来位置的后面一位插入了一个比它大的数字,所以我们只需要考虑插入的数字比原来大的情况。
4,3,2,1 4 , 3 , 2 , 1
4,3,3,2,1 4 , 3 , 3 , 2 , 1
在 3 3 前面插入的 3 3 可以视为在 2 2 前面插入了一个 3 3
我们将这个插入的过程转化成一棵树。树上的每个节点包含两个值, id i d 和 label l a b e l ,分别代表了该结点是第几次插入的和结点的值,而一条边就相当于儿子插入在了父亲的左边
例如, A0 A 0 就相当于一个 (0,0) ( 0 , 0 ) 的结点, {1} { 1 } 就相当于 (0,0)−(1,1) ( 0 , 0 ) − ( 1 , 1 )
那么我们要求的就是满足条件的树的个数
我们设 fi,j f i , j 代表 i i 个结点的树,根节点值为 j j 的树的数量。我们发现 id=1 i d = 1 一定是 id=0 i d = 0 的儿子,那么我们有:
其中 x x 代表子树内有多少个结点, j′ j ′ 是子树根的结点的值, Cx−1i−2 C i − 2 x − 1 枚举了两个块内儿子的次序
我们求一下前缀和,设
这样我们就可以在 O(KN2) O ( K N 2 ) 内解决问题
code:
#include
typedef long long ll;
const int N = 500;
int n, k, mod;
inline ll add( ll a, ll b ) { ll ret = a + b; if( ret >= mod ) ret -= mod; return ret; }
ll fac[N], inv_f[N], f[N][N], s[N][N], c[N][N];
inline ll C( ll n, ll m ) {
return c[n][m];
}
int main()
{
memset( f, 0, sizeof( f ) );
memset( s, 0, sizeof( s ) );
scanf( "%d%d%d", &n, &k, &mod );
for( int i = 0; i <= n; i ++ )
{
c[i][0] = 1;
for( int j = 1; j <= i; j ++ )
c[i][j] = add( c[i-1][j-1], c[i-1][j] );
}
for( int i = 0; i <= k; i ++ )
f[1][i] = 1;
for( int i = 0; i <= k; i ++ )
s[1][i] = !i ? f[1][i] : s[1][i-1] + f[1][i];
for( int i = 2; i <= n + 1; i ++ )
// i : 现在的结点个数
for( int j = 0; j <= k; j ++ )
{
// j : 现在根节点的 label 值
for( int l = 1; l < i; l ++ )
// l : ID=1 子树含有多少个结点
f[i][j] = add( f[i][j], C( i-2, l-1 ) * f[i-l][j] % mod * add( s[l][k], mod - s[l][j] ) % mod ),
// printf( "%lld, %lld, %lld\n", C( i-2, l-1 ), f[i-l][j], add( s[l][k], mod - s[l][j] ) );
s[i][j] = !j ? f[i][j] : add( s[i][j-1], f[i][j] );
// printf( "i:%d, j:%d, f:%lld, s:%lld\n", i, j, f[i][j], s[i][j] );
}
printf( "%lld\n", f[n+1][0] );
return 0;
}