A. Sticker Album
很显然的一道概率dp题,状态转移方程如下: dp[i]=(dp[i+A]+dp[i+A+1]+...+dp[i+B])/(B−A+1)+1dp[i]=(dp[i+A]+dp[i+A+1]+...+dp[i+B])/(B-A+1)+1 d p [ i ] = ( d p [ i + A ] + d p [ i + A + 1 ] + . . . + d p [ i + B ] ) / ( B − A + 1 ) + 1 要留意一下当 A=0A=0 A = 0 时候的特判:dp[i]=(dp[i+A+1]+...+dp[i+B]+B−A+1)/(B−A)dp[i]= (dp[i+A+1]+...+dp[i+B]+B-A+1)/(B-A) d p [ i ] = ( d p [ i + A + 1 ] + . . . + d p [ i + B ] + B − A + 1 ) / ( B − A )
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10 ;
const int INF = 0x3f3f3f3f ;
double dp[ N] = { 0 } ;
int n, A, B;
int main ( ) {
scanf ( "%d %d %d" , & n, & A, & B) ;
if ( A == 0 ) {
double sum = 0 ;
for ( int i = n - 1 ; i >= 0 ; -- i) {
sum - = dp[ i + B + 1 ] ;
sum + = dp[ i + 1 ] ;
dp[ i] = ( sum + B - A + 1 ) / ( B - A) ;
}
printf ( "%.10lf\n" , dp[ 0 ] ) ;
}
else {
double sum = 0 ;
for ( int i = n - 1 ; i >= 0 ; -- i) {
sum - = dp[ i + B + 1 ] ;
sum + = dp[ i + A] ;
dp[ i] = sum / ( B - A + 1 ) + 1.0 ;
}
printf ( "%.10lf\n" , dp[ 0 ] ) ;
}
return 0 ;
}
B. Battleship
签到题。根据题意将10 * 10的矩阵依次用元素覆盖,若覆盖出现重叠则输出NN N ,否则输出YY Y 直接无脑暴力即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10 ;
const int NN = 1e2 + 10 ;
const int INF = 0x3f3f3f3f ;
int n;
bool mp[ NN] [ NN] ;
bool check ( int x, int y) {
if ( x < 1 || x > 10 || y < 1 || y > 10 )
return false ;
return true ;
}
int main ( ) {
scanf ( "%d" , & n) ;
bool flag = true ;
for ( int i = 1 ; i <= n; ++ i) {
int x, y;
int l, op;
scanf ( "%d%d%d%d" , & op, & l, & x, & y) ;
if ( op == 0 )
for ( int j = y; j <= y + l - 1 ; ++ j) {
if ( ! check ( x, j) || mp[ x] [ j] )
flag = false ;
mp[ x] [ j] = true ;
}
else
for ( int j = x; j <= x + l - 1 ; ++ j) {
if ( ! check ( j, y) || mp[ j] [ y] )
flag = false ;
mp[ j] [ y] = true ;
}
}
if ( flag)
puts ( "Y" ) ;
else
puts ( "N" ) ;
return 0 ;
}
C. Concatenating Teams
根据题目意思,对于在 AA A 中的字符串 ai,aja_i,a_j a i , a j 以及一个字符串 ss s ,满足 ai=aj+sa_i=a_j+s a i = a j + s 同样在 BB B 中的字符串 bi,bjb_i,b_j b i , b j 以及一个字符串 cc c ,满足 bi=bj+cb_i=b_j+c b i = b j + c 那么当 s=cs=c s = c 的时候,就会多次构成相同的字符串 利用这一个特性,找到所有 AA A 中的 ss s 以及 BB B 中的 cc c ,对于 s=cs=c s = c 的所有串而言,把这个串排除在答案外就可以了。 可以暴力的用 hashhash h a s h 并进行空间换取时间,也可以使用前缀树与后缀树做 如下代码是暴力 hashhash h a s h ,time:1855ms
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair< ull, ull> pii;
typedef pair< ll, ll> pll;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
const int N = 1e5 + 10 ;
const int M = 1e7 + 10 ;
const int mod = 1e9 + 7 ;
const ull seed = 31 ;
const ll NUM = 5782344 ;
ll gcd ( ll x, ll y) { if ( y == 0 ) return x; return gcd ( y, x % y) ; }
int n, m;
inline bool cmp ( const string& a, const string& b) {
return a. size ( ) < b. size ( ) ;
}
unordered_map< ull, bool> hash_A, hash_B;
unordered_map< ull, bool> pref, suff;
ull prefHash ( string s, int st, int ed) {
ull ans = 0 ;
for ( int i = st; i < ed; ++ i) {
ans = ans * seed + s[ i] - 'a' + 1 ;
}
return ans;
}
ull suffHash ( string s, int st, int ed) {
ull ans = 0 ;
for ( int i = ed - 1 ; i >= st; -- i) {
ans = ans * seed + s[ i] - 'a' + 1 ;
}
return ans;
}
bool a[ N] = { false } , b[ N] = { false } ;
ull Ahash[ N] , Bhash[ N] ;
unordered_map< ull, bool> vis;
vector< pii> vec[ N] ;
int main ( ) {
#ifdef _DEBUG
FILE* _INPUT = freopen ( "input.txt" , "r" , stdin ) ;
#endif
ios: : sync_with_stdio ( false) ; cin. tie ( 0 ) ; cout. tie ( 0 ) ;
cin >> m >> n;
vector< string> A ( m) , B ( n) ;
for ( auto & v : A) cin >> v;
for ( auto & v : B) cin >> v;
sort ( A. begin ( ) , A. end ( ) , cmp) ;
sort ( B. begin ( ) , B. end ( ) , cmp) ;
for ( int i = 0 ; i < m; ++ i) {
if ( ! i) {
ull tmp = prefHash ( A[ i] , 0 , A[ i] . size ( ) ) ;
hash_A[ tmp] = 1 ;
Ahash[ i] = tmp;
continue ;
}
ull ans = 0 ;
for ( int j = 0 ; j < A[ i] . size ( ) ; ++ j) {
ans = ans * seed + A[ i] [ j] - 'a' + 1 ;
if ( hash_A[ ans] ) {
ull tmp = prefHash ( A[ i] , j + 1 , A[ i] . size ( ) ) ;
suff[ tmp] = 1 ;
vec[ i] . push_back ( { tmp, ans } ) ;
}
}
hash_A[ ans] = 1 ;
Ahash[ i] = ans;
}
for ( int i = 0 ; i < n; ++ i) {
if ( ! i) {
ull tmp = suffHash ( B[ i] , 0 , B[ i] . size ( ) ) ;
hash_B[ tmp] = 1 ;
Bhash[ i] = tmp;
continue ;
}
ull ans = 0 ;
for ( int j = B[ i] . size ( ) - 1 ; j >= 0 ; -- j) {
ans = ans * seed + B[ i] [ j] - 'a' + 1 ;
if ( hash_B[ ans] ) {
ull tmp = prefHash ( B[ i] , 0 , j) ;
pref[ tmp] = 1 ;
if ( suff[ tmp] )
b[ i] = 1 , vis[ ans] = 1 ;
}
}
hash_B[ ans] = 1 ;
Bhash[ i] = ans;
}
for ( int i = 0 ; i < n; ++ i) {
if ( vis[ Bhash[ i] ] ) b[ i] = 1 ;
}
vis. clear ( ) ;
for ( int i = 1 ; i < m; ++ i) {
for ( auto v : vec[ i] ) {
if ( pref[ v. first] ) a[ i] = 1 , vis[ v. second] = 1 ;
}
}
for ( int i = 0 ; i < m; ++ i) {
if ( a[ i] ) continue ;
if ( vis[ Ahash[ i] ] ) a[ i] = 1 ;
}
int ans1 = 0 , ans2 = 0 ;
for ( int i = 0 ; i < m; ++ i) ans1 + = ! a[ i] ;
for ( int i = 0 ; i < n; ++ i) ans2 + = ! b[ i] ;
cout << ans1 << " " << ans2 << '\n' ;
return 0 ;
}
E. Party Company
题意:给定一棵树,其中满足父节点的权值 ≥ 子结点的权值,然后给定查询,每次给定一个起点以及权值左区间和右区间,跑图可以跑到权值在这个区间范围以内的点,问最终每个点都各自跑了多少遍 直接每次查询暴力跑图肯定是不合理的。先对于每一次查询,找到最远的父节点 uu u 并且满足 wu≤Rw_u\le R w u ≤ R ,然后每次向这个点记录 LL L 。
因为对于每次查找,权值最大的点即往上的结点只有一个,而满足 wx≥Lw_x\ge L w x ≥ L 的点未知。
待所有的 qq q 查询完后,可以利用树上差分的思想,当遍历到一个点 xx x ,在这个点上存了几个 LL L ,那就将这几个 LL L 进行差分操作。操作完后查询当前结点的值即可。 实现上,对于查询过程中向上找父节点,可以考虑树上倍增的方法节约时间;在查询完后的差分操作,可以考虑使用树状数组,对于当前点 xx x ,将在这个点的所有的 LL L 放入树状数组中。查询的时候便查找当前点 axa_x a x 中 ≤ax\le a_x ≤ a x 的 LL L 有多少个。 每次向下搜索后回溯到当前点,需要清楚掉当前结点记录的所有的 LL L ,详情请看代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N= 1e5 + 10 ;
const int M= 1e7 + 10 ;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
int head[ N] , cntE= 0 ;
struct edge{
int next, to, w;
} e[ M] ;
void add ( int u, int v, int w= 0 ) {
e[ cntE] . next= head[ u] ;
e[ cntE] . to= v;
e[ cntE] . w= w;
head[ u] = cntE++ ;
}
int n, m;
int fa[ N] [ 25 ] = { 0 } , h[ N] , lg[ N] ;
void dfs ( int x, int fx, int dep) {
fa[ x] [ 0 ] = fx;
h[ x] = dep;
for ( int i = head[ x] ; ~ i; i = e[ i] . next) {
int v = e[ i] . to;
dfs ( v, x, dep + 1 ) ;
}
}
void init ( ) {
dfs ( 1 , 0 , 1 ) ;
for ( int j = 1 ; j <= 20 ; ++ j) {
for ( int i = 1 ; i <= n; ++ i) {
fa[ i] [ j] = fa[ fa[ i] [ j - 1 ] ] [ j - 1 ] ;
}
}
lg[ 0 ] = 0 ;
for ( int i = 1 ; i <= N - 10 ; ++ i) {
lg[ i] = lg[ i - 1 ] + ( 1 << lg[ i - 1 ] == i) ;
}
}
vector< int > vec[ N] ;
vector< int > num;
int ans[ N] , a[ N] ;
int get ( int x, int r) {
for ( int i= 20 ; i>= 0 ; -- i) {
if ( fa[ x] [ i] >= 1 && a[ fa[ x] [ i] ] <= r) x= fa[ x] [ i] ;
}
return x;
}
int sum[ N<< 1 ] = { 0 } ;
inline int lowbit ( int x) {
return x& ( - x) ;
}
void ad ( int x, int w) {
for ( int i= x; i<= N- 5 ; i+ = lowbit ( i) ) {
sum[ i] + = w;
}
}
int ask ( int x) {
int ans= 0 ;
while ( x) {
ans+ = sum[ x] ;
x- = lowbit ( x) ;
}
return ans;
}
void dfs ( int x) {
for ( auto v: vec[ x] ) {
ad ( v, 1 ) ;
}
ans[ x] = ask ( a[ x] ) ;
for ( int i= head[ x] ; ~ i; i= e[ i] . next) {
int v= e[ i] . to;
dfs ( v) ;
}
for ( auto v: vec[ x] ) {
ad ( v, - 1 ) ;
}
}
int main ( ) {
scanf ( "%d %d" , & n, & m) ;
memset ( head, - 1 , sizeof ( int ) * ( n+ 10 ) ) ; cntE= 0 ;
for ( int i= 1 ; i<= n; ++ i) {
int x;
rd ( a[ i] ) ; rd ( x) ;
if ( i== x) continue ;
add ( x, i) ;
}
init ( ) ;
while ( m-- ) {
int x, l, r; scanf ( "%d %d %d" , & x, & l, & r) ;
int id= get ( x, r) ;
vec[ id] . push_back ( l) ;
}
dfs ( 1 ) ;
for ( int i= 1 ; i<= n; ++ i) printf ( "%d%s" , ans[ i] , i== n? "\n" : " " ) ;
return 0 ;
}
F. Fastminton
一道简单模拟题 一场三局两胜的比赛 每一局比赛只要有一个人达到55 5 分并且超过对手两分或者直接达到1010 1 0 分,则该人胜利 每一次胜利者下一回合就是发球手,每一局比赛开始时左边的人先发球 若还没有人胜利,则每一次QQ Q 就是询问当前局每个人胜利的场次和当前局所获得的分数,∗* ∗ 号表示该人下一回合发球 若已经有人胜利,则每一次Q就输出当前两人的胜利场次和胜者 具体输出格式参照题目
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10 ;
const int INF = 0x3f3f3f3f ;
char s[ N] ;
int main ( ) {
scanf ( " %s" , s + 1 ) ;
int len = strlen ( s + 1 ) ;
bool flag = false ;
int ls = 0 , rs = 0 , RS = 0 , LS = 0 ;
int win = 0 ;
for ( int i = 1 ; i <= len; ++ i) {
if ( ! flag) {
if ( s[ i] == 'S' ) {
++ ls;
}
else if ( s[ i] == 'R' ) {
++ rs;
flag = true ;
}
bool win = false ;
if ( ls >= 5 && ls - rs >= 2 || ls >= 10 ) {
++ LS;
ls = rs = 0 ;
}
else if ( rs >= 5 && rs - ls >= 2 || rs >= 10 ) {
++ RS;
ls = rs = 0 ;
}
if ( win == 0 ) {
if ( LS >= 2 )
win = 1 ;
else if ( RS >= 2 )
win = 2 ;
}
if ( s[ i] == 'Q' ) {
if ( win == 1 )
printf ( "%d (winner) - %d\n" , LS, RS) ;
else if ( win == 2 )
printf ( "%d - %d (winner)\n" , LS, RS) ;
else {
if ( ! flag)
printf ( "%d (%d*) - %d (%d)\n" , LS, ls, RS, rs) ;
else
printf ( "%d (%d) - %d (%d*)\n" , LS, ls, RS, rs) ;
}
}
}
else {
if ( s[ i] == 'S' ) {
++ rs;
}
else if ( s[ i] == 'R' ) {
++ ls;
flag = false ;
}
if ( ls >= 5 && ls - rs >= 2 || ls >= 10 ) {
++ LS;
ls = rs = 0 ;
}
else if ( rs >= 5 && rs - ls >= 2 || rs >= 10 ) {
++ RS;
ls = rs = 0 ;
}
if ( win == 0 ) {
if ( LS >= 2 )
win = 1 ;
else if ( RS >= 2 )
win = 2 ;
}
if ( s[ i] == 'Q' ) {
if ( win == 1 )
printf ( "%d (winner) - %d\n" , LS, RS) ;
else if ( win == 2 )
printf ( "%d - %d (winner)\n" , LS, RS) ;
else {
if ( ! flag)
printf ( "%d (%d*) - %d (%d)\n" , LS, ls, RS, rs) ;
else
printf ( "%d (%d) - %d (%d*)\n" , LS, ls, RS, rs) ;
}
}
}
}
return 0 ;
}
G. Game Show!
签到题 找到最大前缀和即可 注意根据题意其可以一次操作都不进行,所以最低分一定是100100 1 0 0
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10 ;
const int INF = 0x3f3f3f3f ;
int n;
int a[ N] , sum[ N] ;
int main ( ) {
scanf ( "%d" , & n) ;
for ( int i = 1 ; i <= n; ++ i)
scanf ( "%d" , & a[ i] ) ;
int maxx = - INF;
for ( int i = 1 ; i <= n; ++ i) {
sum[ i] = sum[ i - 1 ] + a[ i] ;
maxx = max ( maxx, sum[ i] ) ;
}
printf ( "%d\n" , max ( maxx + 100 , 100 ) ) ;
return 0 ;
}
H. SBC’s Hangar
首先利用好题目给的一点:排完序后 2∗Wi≤Wi+12*W_i\le W_{i+1} 2 ∗ W i ≤ W i + 1 , 推得 W1+W2+...+Wi<Wi+1W_1+W_2+...+W_i<W_{i+1} W 1 + W 2 + . . . + W i < W i + 1 利用好这个性质就好办了,对于区间 [A,B][A,B] [ A , B ] 由于区间比较大,考虑使用 f(x)f(x) f ( x ) 表示当总重量 ≤x\le x ≤ x 的方案数,则最终所求便是 f(B)−f(A−1)f(B)-f(A-1) f ( B ) − f ( A − 1 ) 在求 f(x,i,k)f(x,i,k) f ( x , i , k ) 的过程中,假设当前遍历到下标 ii i ,对于当前的总重量 xx x ,有两种可能:
不需要 ii i ,从 [i+1,n][i+1,n] [ i + 1 , n ] 当中挑 kk k 个,此时对答案的贡献是 Cn−ikC_{n-i}^{k} C n − i k 需要 ii i , 另外再从 [i+1,n][i+1,n] [ i + 1 , n ] 当中挑 k−1k-1 k − 1 个,此时对答案的贡献是 f(x−Wi,i+1,k−1)f(x-W_i,i+1,k-1) f ( x − W i , i + 1 , k − 1 )
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair< int , int > pii;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
const int N = 3e5 + 10 ;
const int M = 1e7 + 10 ;
const int mod = 1e9 + 7 , inf = 0x3f3f3f3f ;
ll gcd ( ll x, ll y) { if ( y == 0 ) return x; return gcd ( y, x % y) ; }
int head[ N] , cntE = 0 ;
struct edge {
int next, to, w;
} e[ M] ;
void add ( int u, int v, int w = 0 ) {
e[ cntE] . to = v;
e[ cntE] . next = head[ u] ;
e[ cntE] . w = w;
head[ u] = cntE++ ;
}
int n, m;
ll c[ 55 ] [ 55 ] , l, r;
vector< ll> a;
ll solve ( ll x) {
ll ans = 0 ;
int cnt = 0 ;
for ( int i = 0 ; i < n; ++ i) {
if ( a[ i] > x) continue ;
ans + = c[ n - i - 1 ] [ m - cnt] ;
x - = a[ i] ;
++ cnt;
if ( m == cnt) {
++ ans;
break ;
}
}
return ans;
}
int main ( ) {
#ifdef _DEBUG
FILE* _INPUT = freopen ( "input.txt" , "r" , stdin ) ;
#endif
for ( int i = 1 ; i <= 50 ; ++ i) {
c[ i] [ 0 ] = c[ i] [ i] = 1 ;
for ( int j = 1 ; j < i; ++ j) {
c[ i] [ j] = c[ i - 1 ] [ j - 1 ] + c[ i - 1 ] [ j] ;
}
}
rd ( n) , rd ( m) ;
a. resize ( n) ;
for ( auto & v : a) rd ( v) ;
sort ( a. rbegin ( ) , a. rend ( ) ) ;
rd ( l) , rd ( r) ;
printf ( "%lld\n" , solve ( r) - solve ( l - 1 ) ) ;
return 0 ;
}
I. Interactivity
经典的树形dp题,首先题目要求在查询量最小的情况下完成操作。可知最小的查询量是叶子结点的数量,可以证明当所有的叶子结点的数量都知道后,整颗树的权值都出来了。 在需要数量是所有叶子结点的情况下,现考虑两个状态,dp[N][2]dp[N][2] d p [ N ] [ 2 ]
若当前点需要查询,则记录为 dp[i][1]dp[i][1] d p [ i ] [ 1 ] ;否则若不需要查询,则记录为 dp[i][0]dp[i][0] d p [ i ] [ 0 ] 当不需要查询的时候,dp[i][0]=∏dp[v][0]dp[i][0]=\prod dp[v][0] d p [ i ] [ 0 ] = ∏ d p [ v ] [ 0 ] ,其中 vv v 是 ii i 的子结点 当需要查询的时候,dp[i][1]=∏dp[vk][0]∗dp[vj][1]dp[i][1]=\prod dp[v_k][0]*dp[v_j][1] d p [ i ] [ 1 ] = ∏ d p [ v k ] [ 0 ] ∗ d p [ v j ] [ 1 ] ,其中 j≠kj\ne k j = k 最后需要 dp[i][0]+=dp[i][1]dp[i][0]+=dp[i][1] d p [ i ] [ 0 ] + = d p [ i ] [ 1 ] 表示在当前点进行查询; dp[i][1]dp[i][1] d p [ i ] [ 1 ] 表示在当前点未进行查询并且仍欠一个查询。
因为直接对乘数积乘逆元会出错,下面代码采用左右两段来进行乘法运算
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair< int , int > pii;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
const int N = 3e5 + 10 ;
const int M = 1e7 + 10 ;
const int mod = 1e9 + 7 , inf = 0x3f3f3f3f ;
ll gcd ( ll x, ll y) { if ( y == 0 ) return x; return gcd ( y, x % y) ; }
int head[ N] , cntE = 0 ;
struct edge {
int next, to, w;
} e[ M] ;
void add ( int u, int v, int w = 0 ) {
e[ cntE] . to = v;
e[ cntE] . next = head[ u] ;
e[ cntE] . w = w;
head[ u] = cntE++ ;
}
ll fp ( ll x, ll y) {
ll ans = 1 ;
while ( y) {
if ( y & 1 ) ans = ans * x % mod;
x = x * x % mod;
y >>= 1 ;
}
return ans;
}
int n, m;
ll dp[ N] [ 2 ] ;
ll pre[ N] , last[ N] ;
void dfs ( int x) {
if ( ( ~ head[ x] ) == 0 ) {
dp[ x] [ 0 ] = dp[ x] [ 1 ] = 1 ;
return ;
}
dp[ x] [ 0 ] = 1 ;
vector< ll> vec;
for ( int i = head[ x] ; ~ i; i = e[ i] . next) {
int v = e[ i] . to;
dfs ( v) ;
dp[ x] [ 0 ] * = dp[ v] [ 0 ] ;
dp[ x] [ 0 ] % = mod;
vec. push_back ( dp[ v] [ 0 ] ) ;
}
int sz = vec. size ( ) ;
pre[ 0 ] = vec[ 0 ] ; last[ sz - 1 ] = vec[ sz - 1 ] ;
last[ sz] = 1 ;
for ( int i = 1 ; i < sz; ++ i) pre[ i] = pre[ i - 1 ] * vec[ i] % mod;
for ( int i = sz - 2 ; i >= 0 ; -- i) last[ i] = last[ i + 1 ] * vec[ i] % mod;
dp[ x] [ 1 ] = 0 ;
int pos = 0 ;
for ( int i = head[ x] ; ~ i; i = e[ i] . next, ++ pos) {
int v = e[ i] . to;
if ( pos == 0 ) dp[ x] [ 1 ] + = last[ pos + 1 ] * dp[ v] [ 1 ] % mod;
else if ( pos == sz - 1 ) dp[ x] [ 1 ] + = pre[ pos - 1 ] * dp[ v] [ 1 ] % mod;
else dp[ x] [ 1 ] + = pre[ pos - 1 ] * last[ pos + 1 ] % mod * dp[ v] [ 1 ] % mod;
dp[ x] [ 1 ] = dp[ x] [ 1 ] < mod ? dp[ x] [ 1 ] : dp[ x] [ 1 ] - mod;
}
dp[ x] [ 0 ] + = dp[ x] [ 1 ] ;
dp[ x] [ 0 ] = dp[ x] [ 0 ] < mod ? dp[ x] [ 0 ] : dp[ x] [ 0 ] - mod;
}
int main ( ) {
#ifdef _DEBUG
FILE* _INPUT = freopen ( "input.txt" , "r" , stdin ) ;
#endif
rd ( n) ;
memset ( head, - 1 , sizeof ( int ) * ( n + 10 ) ) ; cntE = 0 ;
for ( int i = 2 ; i <= n; ++ i) {
int x; rd ( x) ;
add ( x, i) ;
}
dfs ( 1 ) ;
printf ( "%lld\n" , dp[ 1 ] [ 0 ] ) ;
return 0 ;
}
K. Between Us
根据题目意思,每个人只有两个阵营可选, 设 ai=0a_i=0 a i = 0 表示 ii i 选择第一个阵营, ai=1a_i=1 a i = 1 表示第二个阵营 现考虑 ii i 有奇数个朋友,朋友数量为 kk k ,因为奇=奇+偶
假设 ai=0a_i=0 a i = 0 则必须有奇数个 aj=0a_j=0 a j = 0 ,剩下偶数个 aj=1a_j=1 a j = 1 假设 ai=1a_i=1 a i = 1 则必须有奇数个 aj=1a_j=1 a j = 1 ,剩下偶数个 aj=0a_j=0 a j = 0 由上可推出一个规律 aj1⊕aj2...⊕ajk=aia_{j_1}\oplus a_{j_2}...\oplus a_{j_k}=a_i a j 1 ⊕ a j 2 . . . ⊕ a j k = a i
现考虑朋友数量为偶数,因为奇+奇=偶:
无论 aia_i a i 为何值,必定是奇数个 aj=1a_j=1 a j = 1 ,奇数个 aj=0a_j=0 a j = 0 所以满足 aj1⊕aj2...⊕ajk=1a_{j_1}\oplus a_{j_2}...\oplus a_{j_k}=1 a j 1 ⊕ a j 2 . . . ⊕ a j k = 1
当所有的方程式出来后,直接看方程是否有解即可,最终是一个 n∗(n+1)n*(n+1) n ∗ ( n + 1 ) 增广矩阵,先进行高斯消元,而后判断:若当前行 ai,1...ai,na_{i,1}...a_{i,n} a i , 1 . . . a i , n 都为0并且 ai,n+1a_{i,n+1} a i , n + 1 不为0,此时无解。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair< int , int > pii;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
const int N = 1e2 + 10 ;
const int M = 1e7 + 10 ;
const int mod = 1e9 + 7 , inf = 0x3f3f3f3f ;
ll gcd ( ll x, ll y) { if ( y == 0 ) return x; return gcd ( y, x % y) ; }
int head[ N] , cntE = 0 ;
struct edge {
int next, to, w;
} e[ M] ;
void add ( int u, int v, int w = 0 ) {
e[ cntE] . to = v;
e[ cntE] . next = head[ u] ;
e[ cntE] . w = w;
head[ u] = cntE++ ;
}
int n, m;
int d[ N] = { 0 } ;
bitset< N << 1 > a[ N] ;
int Gauss_rev ( int n) {
int rank = 0 ;
for ( int i = 1 ; i <= n; i++ ) {
for ( int j = i; j <= n; j++ ) {
if ( a[ j] [ i] ) {
swap ( a[ i] , a[ j] ) ;
break ;
}
}
if ( ! a[ i] [ i] ) continue ;
else ++ rank;
for ( int j = 1 ; j <= n; j++ ) {
if ( a[ j] [ i] && j != i) {
a[ j] ^ = a[ i] ;
}
}
}
for ( int i = 1 ; i <= n; ++ i) {
if ( ! a[ i] [ n + 1 ] ) continue ;
bool flag = false;
for ( int j = 1 ; j <= n; ++ j) {
if ( a[ i] [ j] ) {
flag = true;
break ;
}
}
if ( ! flag) return 0 ;
}
return 1 ;
}
vector< int > G[ N] ;
int main ( ) {
#ifdef _DEBUG
FILE* _INPUT = freopen ( "input.txt" , "r" , stdin ) ;
#endif
rd ( n) ; rd ( m) ;
while ( m-- ) {
int x, y; rd ( x) , rd ( y) ;
G[ x] . push_back ( y) ;
G[ y] . push_back ( x) ;
}
for ( int i = 1 ; i <= n; ++ i) {
a[ i] . reset ( ) ;
for ( auto v : G[ i] ) a[ i] [ v] = 1 ;
if ( G[ i] . size ( ) & 1 ) a[ i] [ i] = 1 ;
else a[ i] [ n + 1 ] = 1 ;
}
if ( ! Gauss_rev ( n) ) puts ( "N" ) ;
else puts ( "Y" ) ;
return 0 ;
}
L. Lavaspar
这题考虑暴力的做即可 对每个单词逐个处理 从上至下从左至右遍历所有点,对于一个点,可以选择四个方向扫描:右边,下边,左下,右下 每次扫描的时候记录好字母出现次数,全部符合便标记扫描的所有位置
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10 ;
const int INF = 0x3f3f3f3f ;
int l, c, vis[ 50 ] [ 50 ] , cnt[ 50 ] [ 50 ] ;
char a[ 50 ] [ 50 ] ;
char s[ 50 ] ;
int snum[ 26 ] , temp[ 26 ] ;
bool check ( ) {
for ( int i = 0 ; i < 26 ; ++ i)
if ( snum[ i] != temp[ i] ) return false;
return true;
}
void work ( int si, int sj, int len, int flag) {
if ( flag == 1 ) {
for ( int k = sj; k <= sj + len - 1 ; ++ k) vis[ si] [ k] = 1 ;
}
else if ( flag == 2 ) {
for ( int k = si; k <= si + len - 1 ; ++ k) vis[ k] [ sj] = 1 ;
}
else if ( flag == 3 ) {
for ( int k = 0 ; k < len; ++ k) vis[ si + k] [ sj + k] = 1 ;
}
else {
for ( int k = 0 ; k < len; ++ k) vis[ si + k] [ sj - k] = 1 ;
}
}
int main ( ) {
scanf ( "%d%d" , & l, & c) ;
for ( int i = 1 ; i <= l; ++ i)
scanf ( "%s" , a[ i] + 1 ) ;
int n; scanf ( "%d" , & n) ;
while ( n-- ) {
memset ( vis, 0 , sizeof vis) ;
memset ( snum, 0 , sizeof snum) ;
scanf ( "%s" , s) ;
int len = strlen ( s) ;
for ( int i = 0 ; i < len; ++ i) snum[ s[ i] - 'A' ] ++ ;
for ( int i = 1 ; i <= l; ++ i) {
for ( int j = 1 ; j <= c; ++ j) {
memset ( temp, 0 , sizeof temp) ;
if ( c - j + 1 >= len) {
for ( int k = j; k <= j + len - 1 ; ++ k) temp[ a[ i] [ k] - 'A' ] ++ ;
if ( check ( ) ) work ( i, j, len, 1 ) ;
}
memset ( temp, 0 , sizeof temp) ;
if ( l - i + 1 >= len) {
for ( int k = i; k <= i + len - 1 ; ++ k) temp[ a[ k] [ j] - 'A' ] ++ ;
if ( check ( ) ) work ( i, j, len, 2 ) ;
}
memset ( temp, 0 , sizeof temp) ;
if ( min ( c - j + 1 , l - i + 1 ) >= len) {
for ( int k = 0 ; k < len; ++ k) temp[ a[ i + k] [ j + k] - 'A' ] ++ ;
if ( check ( ) ) work ( i, j, len, 3 ) ;
}
memset ( temp, 0 , sizeof temp) ;
if ( min ( j, l - i + 1 ) >= len) {
for ( int k = 0 ; k < len; ++ k) temp[ a[ i + k] [ j - k] - 'A' ] ++ ;
if ( check ( ) ) work ( i, j, len, 4 ) ;
}
}
}
for ( int i = 1 ; i <= l; ++ i)
for ( int j = 1 ; j <= c; ++ j)
if ( vis[ i] [ j] ) cnt[ i] [ j] ++ ;
}
int ans = 0 ;
for ( int i = 1 ; i <= l; ++ i) {
for ( int j = 1 ; j <= c; ++ j) {
if ( cnt[ i] [ j] > 1 ) ans++ ;
}
}
printf ( "%d\n" , ans) ;
return 0 ;
}
M. Machine Gun
显然,对于每个点,有两个边界,分别是斜率 k=12k=\frac{1}{2} k = 2 1 以及 k=−12k=-\frac{1}{2} k = − 2 1 , 令 x=0x=0 x = 0 ,求出对应边界的 yy y 值 给定一个查询的点 x,yx,y x , y ,求出该点的上界 y1=2∗y−x,y2=2∗y+xy_1=2*y-x,y_2=2*y+x y 1 = 2 ∗ y − x , y 2 = 2 ∗ y + x 此后便化成了个二维偏序问题,现假设先按照上界排,对于每个上界的 y1y_1 y 1 值,在这个值当中存放了右边点中上界点 ≤y1\le y_1 ≤ y 1 的所有点,对这个所有点进行排序后筛选出下界 ≥y2\ge y_2 ≥ y 2 的点,按照题目的要求操作即可。 问题是该怎么去存上界 ≤y1\le y_1 ≤ y 1 的所有点,对每一个 y1y_1 y 1 都存是不现实的,时间不允许,空间也不允许。可以使用主席树来继承。 下面考虑一种方法,使用树状数组来存点,以优化时间和空间。详情看代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair< ll, int > pii;
typedef pair< ll, ll> pll;
template< typename T>
inline void rd ( T& x)
{
int tmp = 1 ; char c = getchar ( ) ; x = 0 ;
while ( c > '9' || c < '0' ) { if ( c == '-' ) tmp = - 1 ; c = getchar ( ) ; }
while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar ( ) ; }
x * = tmp;
}
const int N = 2e5 + 10 ;
const int M = 1e7 + 10 ;
const int mod = 1e9 + 7 ;
const ll inf = 1e16 ;
const ll NUM = 5782344 ;
ll gcd ( ll x, ll y) { if ( y == 0 ) return x; return gcd ( y, x % y) ; }
vector< pii> vec[ N] ;
inline int lowbit ( int x) {
return x & ( - x) ;
}
void add ( int x, ll y, int num) {
while ( x <= N - 10 ) {
vec[ x] . push_back ( { y, num } ) ;
x + = lowbit ( x) ;
}
}
vector< int > get ( int x, ll y) {
vector< int > ans;
while ( x) {
for ( auto v : vec[ x] ) {
if ( v. first < y) break ;
ans. push_back ( v. second) ;
}
x - = lowbit ( x) ;
}
return ans;
}
int n, m;
struct node {
ll l, r;
int id;
} a[ N] ;
ll fac[ N] ;
ll b[ N] ;
int main ( ) {
#ifdef _DEBUG
FILE* _INPUT = freopen ( "input.txt" , "r" , stdin ) ;
#endif
rd ( n) , rd ( m) ;
for ( int i = 1 ; i <= n; ++ i) {
ll x, y; rd ( x) , rd ( y) ;
b[ i] = a[ i] . l = 2LL * y - x;
a[ i] . r = 2LL * y + x;
a[ i] . id = i;
}
sort ( b + 1 , b + 1 + n) ; int n1 = n;
n1 = unique ( b + 1 , b + 1 + n) - b - 1 ;
for ( int i = 1 ; i <= n; ++ i) {
int pos = lower_bound ( b + 1 , b + 1 + n1, a[ i] . l) - b;
add ( pos , a[ i] . r, i) ;
}
for ( int i = 0 ; i <= N - 5 ; ++ i) sort ( vec[ i] . rbegin ( ) , vec[ i] . rend ( ) ) ;
fac[ 0 ] = 1 ;
for ( int i = 1 ; i <= N - 5 ; ++ i) fac[ i] = fac[ i - 1 ] * NUM % mod;
ll p = 0 ;
while ( m-- ) {
ll aa, bb; rd ( aa) , rd ( bb) ;
ll x = - 1LL - ( ( p + aa) % mod) ;
ll y = ( p + bb) % mod;
ll tmpl = y * 2 - x;
ll tmpr = y * 2 + x;
int posl = upper_bound ( b + 1 , b + 1 + n1, tmpl) - b - 1 ;
vector< int > vec1 = get ( posl, tmpr) ;
sort ( vec1. begin ( ) , vec1. end ( ) ) ;
int cnt = 0 ;
ll ans = 0 ;
for ( auto v : vec1) {
ans + = 1LL * v * fac[ cnt++ ] % mod;
ans = ans < mod ? ans : ans - mod;
}
printf ( "%lld\n" , ans) ;
p = ans;
}
return 0 ;
}
N. Number Multiplication
对于这题可以考虑暴力的做法,对于 cic_i c i 至多 101510^{15} 1 0 1 5 ,进行欧拉筛法就需要进行到 1015<31622777\sqrt{10^{15}}<31622777 1 0 1 5 < 3 1 6 2 2 7 7 7 对于 3e73e7 3 e 7 的线性筛法时间还是够的,后面就根据每个数进行质因数分解 因为题目给出的质因子是从小到大排,对每个质因数分解后直接对号入座即可。 为了避免超时加一个优化,使用 setset s e t 将所有质因子的下标装进去,若已经知道该下标的值,则删去从 setset s e t 中删除这个点;若 setset s e t 空了直接跳出。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair< int , int > pii;
const int N = 1e3 + 10 ;
const int INF = 0x3f3f3f3f ;
const int M = 31622776 + 10 ;
inline ll rd ( ) {
ll x = 0 , f = 1 ;
char ch = getchar ( ) ;
while ( ch < '0' || ch > '9' ) {
if ( ch == '-' )
f = - 1 ;
ch = getchar ( ) ;
}
while ( ch >= '0' && ch <= '9' ) {
x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ) ;
ch = getchar ( ) ;
}
return x * f;
}
int n, m, k;
int prime[ M] , cnt = 0 ;
bool isprime[ M] ;
void init ( ) {
for ( int i = 2 ; i <= M - 10 ; ++ i) {
if ( ! isprime[ i] ) prime[ ++ cnt] = i;
for ( int j = 1 ; j <= cnt && prime[ j] * i <= M - 10 ; ++ j) {
isprime[ i * prime[ j] ] = true;
if ( i % prime[ j] == 0 ) break ;
}
}
}
ll c[ N] ;
struct node {
ll id, cnt;
node ( ll id = 0 , ll cnt = 0 ) : id ( id) , cnt ( cnt) { }
bool friend operator< ( const node& a, const node& b) {
return a. cnt < b. cnt || a. cnt == b. cnt && a. id < b. id;
}
} ;
vector< node> vec[ N] ;
vector< node> G[ N] ;
ll p[ N] ;
int main ( ) {
init ( ) ;
m = rd ( ) , n = rd ( ) , k = rd ( ) ;
set< int > s;
for ( int i = 1 ; i <= m; ++ i) s. insert ( i) ;
for ( int i = 1 ; i <= n; ++ i) c[ i] = rd ( ) ;
for ( int i = 1 ; i <= k; ++ i) {
int x, y, d; x = rd ( ) , y = rd ( ) , d = rd ( ) ;
vec[ y] . push_back ( node ( x, d) ) ;
}
for ( int i = 1 ; i <= n; ++ i) {
ll tmp = c[ i] ;
G[ i] . resize ( vec[ i] . size ( ) ) ; int pos = 0 ;
for ( int j = 1 ; j <= cnt && 1LL * prime[ j] * prime[ j] <= tmp; ++ j) {
if ( tmp % prime[ j] == 0 ) {
int tot = 0 ;
while ( tmp % prime[ j] == 0 ) tmp / = prime[ j] , ++ tot;
G[ i] [ pos++ ] = ( node ( prime[ j] , tot) ) ;
}
}
if ( tmp > 1 ) G[ i] [ pos++ ] = ( node ( tmp, 1 ) ) ;
sort ( vec[ i] . begin ( ) , vec[ i] . end ( ) ) ;
sort ( G[ i] . begin ( ) , G[ i] . end ( ) ) ;
for ( int j = 0 ; j < vec[ i] . size ( ) ; ++ j) {
p[ vec[ i] [ j] . id] = G[ i] [ j] . id;
auto v = s. find ( vec[ i] [ j] . id) ;
if ( v != s. end ( ) ) s. erase ( v) ;
}
if ( s. empty ( ) ) break ;
}
for ( int i = 1 ; i <= m; ++ i) printf ( "%lld%s" , p[ i] , i == m ? "\n" : " " ) ;
return 0 ;
}