并没有什么想说的,但是要保持格式=w=
CSDN最近一直换界面,BUG频出不止……
还me代码缩进!!
BZOJ4291传送门
给定 N N 个数,请从中选出若干个数,使得总和为偶数,请最大化这个总和
范围: N≤106,ai≤1000 N ≤ 10 6 , a i ≤ 1000
撒币贪心qwq
#include
#include
#include
using namespace std ;
int N , oddcnt , ans , minodd = 20031109 ;
int main(){
scanf( "%d" , &N ) ;
for( int i = 1 , t ; i <= N ; i ++ ){
scanf( "%d" , &t ) , ans += t ;
if( t&1 ){
oddcnt ++ ;
minodd = min( minodd , t ) ;
}
} if( N == 1 && oddcnt == 1 ) puts( "NIESTETY" ) ;
else printf( "%d" , ans - ( oddcnt&1 ) * minodd ) ;
}
BZOJ4292传送门
对于一个正整数 n n ,定义 f(n) f ( n ) 为它十进制下每一位数字的平方的和。现在给定三个正整数 k,a,b k , a , b ,请求出满足 a≤n≤b a ≤ n ≤ b 且 k∗f(n)=n k ∗ f ( n ) = n 的 n n 的个数
范围: a,b,k≤1018 a , b , k ≤ 10 18
第一眼以为是数位dp
然而限制这么强,直接都是等号了……
撒币枚举qwq
#include
#include
#include
using namespace std ;
int ans ;
long long K , A , B ;
bool check( long long n , int sums ){
while( n ){
int t = n%10 ; n /= 10 ;
sums -= t * t ;
} return sums == 0 ;
}
int main(){
scanf( "%lld%lld%lld" , &K , &A , &B ) ;
for( int fn = 0 ; fn <= 81 * 18 ; fn ++ ){
if( K * fn > B ) break ;
if( K * fn < A ) continue ;
ans += check( K * fn , fn ) ;
} printf( "%d" , ans ) ;
}
BZOJ4293传送门
农夫Byteasar买了一片 N N 亩的土地,他要在这上面种草。
他在每一亩土地上都种植了一种独一无二的草,其中,第 i i 亩土地的草每天会长高 a[i] a [ i ] 厘米。
Byteasar一共会进行 M M 次收割,其中第 i i 次收割在第 d[i] d [ i ] 天,并把所有高度大于等于 b[i] b [ i ] 的部分全部割去。Byteasar想知道,每次收割得到的草的高度总和是多少,你能帮帮他吗?不能
范围: a[i]≤106 a [ i ] ≤ 10 6 , d[i],b[i]≤1012 d [ i ] , b [ i ] ≤ 10 12 , N,M≤500000 N , M ≤ 500000
约定:保证割草的时间递增,且任意时刻草的高度不超过 1012 10 12
其实大体的思路是不难想的,只是实现的时候,需要选择合适的方法
首先这些草,顺序是无所谓的,为了方便维护,我们显然可以将所有草按照生长速度排序
然后可以发现,一次就会割掉一段后缀,并且把后缀的高度全部改成一个值,并且获得多余部分的收益
不难想到用线段树维护
线段树呢,在修改的时候,顺便就把时间以及高度带入进去,进一个区间,就把那个区间的信息更新到现在时刻。然后这样的话,判断割草和计算贡献都很方便
#include
#include
#include
using namespace std ;
int N , M , a[1000005] ;
struct Node{
long long sumV , date , sumH ;
long long minH , maxH , flag , flag_date ;
int lf , rg ;
Node *ch[2] ;
void change_Date( long long now ){
if( now == date ) return ;
sumH += ( now - date ) * sumV ;
minH += ( now - date ) * a[lf] ;
maxH += ( now - date ) * a[rg] ;
date = now ;
}
void update(){
sumH = ch[0]->sumH + ch[1]->sumH ;
minH = ch[0]->minH , maxH = ch[1]->maxH ;
}
void pushdown(){
if( !flag_date ) return ;
ch[0]->flag_date = ch[0]->date = flag_date ;
ch[1]->flag_date = ch[1]->date = flag_date ;
ch[0]->sumH = flag * ( ch[0]->rg - ch[0]->lf + 1 ) ;
ch[1]->sumH = flag * ( ch[1]->rg - ch[1]->lf + 1 ) ;
ch[0]->flag = ch[0]->minH = ch[0]->maxH = flag ;
ch[1]->flag = ch[1]->minH = ch[1]->maxH = flag ;
flag_date = 0 ;
}
}*root , w[2000005] , *tw = w ;
Node *build( int lf , int rg ){
Node *nd = ++tw ;
if( lf == rg ){
nd->lf = nd->rg = lf ;
nd->sumV = a[lf] ; return nd ;
} int mid = ( lf + rg ) >> 1 ;
nd->ch[0] = build( lf , mid ) ;
nd->ch[1] = build( mid+1,rg ) ;
nd->sumV = nd->ch[0]->sumV + nd->ch[1]->sumV ;
nd->lf = lf , nd->rg = rg ;
return nd ;
}
long long Modify( Node *nd , int lf , int rg , long long date , long long H ){
nd->change_Date( date ) ;
if( nd->maxH <= H ) return 0 ;
if( nd->minH >= H ){
long long rt = nd->sumH - H * ( rg - lf + 1 ) ;
nd->sumH = H * ( rg - lf + 1 ) ;
nd->minH = nd->maxH = nd->flag = H ;
nd->flag_date = date ;
return rt ;
} int mid = ( lf + rg ) >> 1 ;
nd->pushdown() ;
long long rt = Modify( nd->ch[0] , lf , mid , date , H ) +
Modify( nd->ch[1] , mid+1,rg , date , H ) ;
nd->update() ;
return rt ;
}
void solve(){
long long b , d ;
for( int i = 1 ; i <= M ; i ++ ){
scanf( "%lld%lld" , &d , &b ) ;
printf( "%lld\n" , Modify( root , 1 , N , d , b ) ) ;
}
}
int main(){
scanf( "%d%d" , &N , &M ) ;
for( int i = 1 ; i <= N ; i ++ ) scanf( "%d" , &a[i] ) ;
sort( a + 1 , a + N + 1 ) ;
root = build( 1 , N ) ; solve() ;
}
BZOJ4294传送门
众所周知,斐波那契数列F满足:
f0=0,f1=1,fm=fm−1+fm−2(m≥2) f 0 = 0 , f 1 = 1 , f m = f m − 1 + f m − 2 ( m ≥ 2 )
现在给出一个数字串 S S ,请找到一个 k k 使得 fk f k 以 S S 为结尾
(特别的,如果 k≤10100 k ≤ 10 100 范围内没有答案,输出NIE)
范围: |S|≤18 | S | ≤ 18
看到这个题呢,并没有什么很明显的算法偏向
me就在想,如果只看Fibonacci数列的末尾几位数,它这个东西可能具有循环节
于是me打了一个30的表,并没有发现末尾位有循环= =…
然后就去看了题解……
然后是这样的:Fib数列在模 10m 10 m 意义下,具有长度为 6∗10m 6 ∗ 10 m 的循环节(所以题目上那个 10100 10 100 就是吓人用的)
这显然可以通过定义 f[i][j] f [ i ] [ j ] 表示,在模某个数字的意义下,当前为 i i ,上一个为 j j 打表得到(打表打少了hhhhh)
然后就可以搜索,从低位到高位按位确定,并用矩阵快速幂来判断当前的Fib是否符合条件就好了
感性上觉得这个算法还是很快的
假装此处有代码
(me懒,并不想写这个题的代码)
me最最最讨厌细节题啦!!!QAQ
BZOJ4295传送门
有 N N 个人在轮流玩赌博机,一开始编号为 i i 的人有 a[i] a [ i ] 元钱。赌博机可以抽象为一个长度为 m m 的仅包含 1 1 和 −1 − 1 的序列,若抽到 1 1 ,那么你将得到 1 1 块钱;若抽到 −1 − 1 ,你将输掉 1 1 块钱。
第 1 1 局,第 1 1 个人会抽到序列中的第 1 1 项;第 2 2 局,第 2 2 个人会抽到序列中的第 2 2 项;第 3 3 局,第 3 3 个人会抽到序列中的第 3 3 项……即:第 i i 个人抽完后轮到第 i+1 i + 1 个人去抽,特别地,第 n n 个人抽完后轮到第 1 1 个人去抽。序列第 i i 项被抽到之后,下一个被抽到的将会是第 i+1 i + 1 项,特别地,序列第 m m 项被抽到之后,下一个被抽到的将会是第 1 1 项。
如果在某一轮,有个人输光了所有的钱,那么这场赌博游戏就会结束,请求出游戏在哪一轮结束,或者判断这个游戏会永远进行下去
范围: N,M,a[i]≤1000000 N , M , a [ i ] ≤ 1000000
这题细节多的....
首先很显然的,这个抽奖应该具有循环节
然后还可以发现,如果是 n n 个人抽长度为 m m 的序列,那么每个人能抽到的位置是 m/gcd(n,m) m / gcd ( n , m )
(比如说 6 6 个人抽长为 4 4 的序列,第一个人只能抽到 1 1 号或 3 3 号)
那么我们可以把这一些人都提出来一起处理(因为他们的抽奖序列是相同的,只是开始位置不同)
一个人抽到没有钱,要么是因为:一个循环的总和为负,经过几个循环之后在某一个位置停下来;要么就是在第一个循环就停下来
大概把这个序列画一个图出来,发现我们需要维护「前缀和」以及「从每个位置开始经过一个循环的最低值」(这个可以用单调队列)
另外,为了能够快速的知道「每一个值会在之后的什么位置出现」,需要倒着处理,处理过程中记录每个位置最后的出现位置
大概就是这些吧,反正需要想清楚再写
#include
#include
#include
#include
#include
#define las(x) las[x+1000000]
using namespace std ;
const int maxn = 1000005 ;
char ss[maxn] ;
int N , M , a[maxn] , b[maxn] ;
bool vis[maxn] ;
int id[maxn] , que[2*maxn] , fr , ba ;
int nb[maxn] , sum[2*maxn] , mn[maxn] , las[2*maxn] ;
void solve(){
int len , All ;
long long ans = 1LL << 56 ;
for( int i = 0 ; !vis[i] && i < M ; i ++ ){
//get sequence
len = 0 ;
for( int j = i ; !vis[j] ; j = ( j + N )%M ){
nb[++len] = b[j] , vis[j] = true , id[len] = j ;
// printf( "%d " , nb[len] ) ;
}
// puts( "" ) ;
//get prefix_min_value & prefix_sum
fr = 1 , ba = 0 ;
for( int j = 1 ; j <= len ; j ++ ){
sum[j] = sum[j-1] + nb[j] ;
while( ba >= fr && sum[ que[ba] ] >= sum[j] ) ba -- ;
que[++ba] = j ;
} for( int j = 1 ; j <= len ; j ++ ){
while( que[fr] < j - 1 ) fr ++ ;
mn[j] = min( sum[ que[fr] ] - sum[j-1] , 0 ) ;
// printf( "mn[%d] = %d\n" , j , mn[j] ) ;
sum[j+len] = sum[j+len-1] + nb[j] ;
while( ba >= fr && sum[ que[ba] ] >= sum[j+len] ) ba -- ;
que[++ba] = j + len ;
} All = - sum[len] ;
//calc_ans
for( int j = 2 * len ; j > len ; j -- ) las( sum[j] ) = j ;
for( int j = len ; j ; j -- ){
// printf( "j = %d:\n" , j ) ;
las( sum[j] ) = j ;
for( int k = id[j] ; k < N ; k += M ){
// printf( "k = %d:\n" , k ) ;
int val = a[k] ;
if( val + mn[j] > 0 ) { // will not GG at fitst round
if( All <= 0 ) continue ;
long long rd = ceil( 1.0 * ( val + mn[j] ) / All ) , tmp = rd * len ;
tmp += las( sum[j-1] - ( val - rd * All ) ) - j ;
// printf( "rd = %lld , tmp = %lld\n" , rd , tmp ) ;
ans = min( ans , tmp * N + k + 1 ) ;
} else {
ans = min( ans , 1LL * N * ( las( sum[j-1] - val ) - j ) + k + 1 ) ;
// printf( "pos:: %d \n" , las( sum[j-1] - val ) ) ;
}
} //puts( "") ;
}
} printf( "%lld" , ans == ( 1LL << 56 ) ? -1 : ans ) ;
}
int main(){
scanf( "%d" , &N ) ;
for( int i = 0 ; i < N ; i ++ ) scanf( "%d" , &a[i] ) ;
scanf( "%d" , &M ) ; scanf( "%s" , ss ) ;
for( int i = 0 ; i < M ; i ++ ) b[i] = ( ss[i] == 'W' ? 1 : -1 ) ;
solve() ;
}
BZOJ4296传送门
给定一张 n n 个点 m m 条边的无向图,请找到一个点数最多的点集 S S ,满足:
输出字典序最小的解(当me写到这里时,还没有SPJ,因此只有这样才能过)
范围: n,m≤200000,d<n n , m ≤ 200000 , d < n
约定:无自环
直接用个队列,把点度小于 d d 的点全删了
然后dfs找合法连通块就ok
#include
#include
#include
#define GG return (void)puts( "NIE" ) ;
using namespace std ;
int N , M , D , deg[200005] , tp , head[200005] ;
struct Path{
int pre , to ;
}p[400005] ;
void In( int t1 , int t2 ){
deg[t1] ++ , deg[t2] ++ ;
p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}
bool vis[200005] ;
int que[200005] , ans[200005] , ans_cnt ;
int sta[200005] , topp ;
void dfs( int u ){
vis[u] = true , sta[++topp] = u ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( !vis[v] ) dfs( v ) ;
}
}
void solve(){
int fr = 1 , ba = 0 ;
for( int i = 1 ; i <= N ; i ++ )
if( deg[i] < D ) que[++ba] = i , vis[i] = true ;
while( ba >= fr ){
int u = que[fr++] ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
deg[v] -- ;
if( !vis[v] && deg[v] < D )
que[++ba] = v , vis[v] = true ;
}
} for( int i = 1 ; i <= N ; i ++ ) if( !vis[i] ){
topp = 0 , dfs( i ) ;
if( topp > ans_cnt ){
ans_cnt = topp ;
memcpy( ans , sta , ( topp + 2 ) * sizeof( int ) ) ;
}
} if( !ans_cnt ) GG ;
printf( "%d\n" , ans_cnt ) ;
sort( ans + 1 , ans + ans_cnt + 1 ) ;
for( int i = 1 ; i <= ans_cnt ; i ++ )
printf( "%d " , ans[i] ) ;
}
int main(){
scanf( "%d%d%d" , &N , &M , &D ) ;
for( int i = 1 , u , v ; i <= M ; i ++ ){
scanf( "%d%d" , &u , &v ) ;
In( u , v ) ;
} solve() ;
}