E.Watermelon
思路:设sum(x)为前x个数的前缀和,我们先找到最大值的位置p,然后我开始枚举轮次x,假设经过了x满轮到了第p-1个人,我们求出当前吃的最少的瓜为Min=x*(n-1)+x* ap+p-1,当前吃的最多的瓜是Max=x* sum(n)+sum(p-1),如果Min<=m<=Max,那么肯定就肯定输出YES了(想一想,为什么),如果不满足我们一直枚举x,直到Min>m结束。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn= 1e6 + 10 ;
int a[ maxn] , n, m;
ll sum[ maxn] ;
int main ( )
{
int T;
scanf ( "%d" , & T) ;
while ( T-- )
{
int p= 0 , mx0= 0 ;
scanf ( "%d%d" , & n, & m) ;
for ( int i= 1 ; i<= n; i++ )
{
scanf ( "%d" , & a[ i] ) ;
sum[ i] = sum[ i- 1 ] + a[ i] ;
if ( a[ i] > mx0)
p= i, mx0= a[ i] ;
}
int flag= 0 ;
ll x= 0 ;
while ( 1 )
{
ll mn= x* ( n- 1 ) + x* mx0+ p- 1 ;
ll mx= x* sum[ n] + sum[ p- 1 ] ;
if ( mn> m)
break ;
if ( m>= mn&& m<= mx)
{
flag= 1 ;
break ;
}
x++ ;
}
if ( n== 1 )
flag= 1 ;
if ( flag)
puts ( "YES" ) ;
else
puts ( "NO" ) ;
}
}
G.Truthman or Fakeman
这题我用2-sat写了个假算法…看了别人AC代码补的
思路:我们把每个点u拆成u,u+n,u表示说真话,u+n表示说假话,首先 u v 1,那么u v要么同真,要么同假,我们用并查集分别连接 u,v 和 u+n,v+n,如果 u v 0,那么u v肯定一真一假,我们用并查集分别连接u,v+n 和 u+n,v,完成连接后,我们枚举每个点 i ,用并查集判断 i 和 i+n 是否联通,如果联通,肯定是无解,如果不联通,就看哪个联通块真话的人多,就选择哪个联通快。
#include <bits/stdc++.h>
using namespace std;
const int maxn= 2e5 + 10 ;
int vis[ maxn] , p[ maxn] , sz[ maxn] , n;
char s[ maxn] ;
int find ( int x)
{
if ( p[ x] != x)
p[ x] = find ( p[ x] ) ;
return p[ x] ;
}
bool join ( int x, int y)
{
x= find ( x) ;
y= find ( y) ;
if ( x== y)
return false;
sz[ y] + = sz[ x] ;
p[ x] = y;
return true;
}
bool ok ( )
{
for ( int i= 1 ; i<= n; i++ )
{
int x= find ( i) ;
int y= find ( i+ n) ;
if ( x== y)
return false;
if ( ! vis[ x] )
{
if ( sz[ x] > sz[ y] )
vis[ x] = 1 , vis[ y] = - 1 ;
else
vis[ x] = - 1 , vis[ y] = 1 ;
}
if ( vis[ x] == 1 )
s[ i- 1 ] = '1' ;
else
s[ i- 1 ] = '0' ;
}
s[ n] = 0 ;
puts ( s) ;
return true;
}
int main ( )
{
int T;
cin>> T;
while ( T-- )
{
int m, u, v, w;
scanf ( "%d%d" , & n, & m) ;
for ( int i= 1 ; i<= 2 * n; i++ )
vis[ i] = 0 , p[ i] = i, sz[ i] = i<= n;
for ( int i= 1 ; i<= m; i++ )
{
scanf ( "%d%d%d" , & u, & v, & w) ;
if ( w)
{
join ( u, v) ;
join ( u+ n, v+ n) ;
}
else
{
join ( u, v+ n) ;
join ( u+ n, v) ;
}
}
if ( ! ok ( ) )
puts ( "-1" ) ;
}
}
H.Chat
思路:设d[i][j]为前 i 天女神的生气度为 j 最小总上线时间,对于第 i+1 天,我们枚举这一天女神新增的生气度 p ,求出新增 p 需要最小的上线时间time ,我们枚举再生气度 j,显然转移方程:d[i+1][j+p]=min(d[i+1][j+p], d[i][j]+time)
#include <bits/stdc++.h>
using namespace std;
const int maxn= 210 ;
int d[ maxn] [ maxn] , n, m, k, inf= 1e8 ;
char s[ maxn] ;
int q[ 201 ] ;
int gao ( int N)
{
if ( ! N) return 0 ;
int h= 0 , t= 1 , ans= inf;
for ( int i= 1 ; i<= m; i++ )
if ( s[ i] == '1' )
{
q[ ++ h] = i;
if ( h- t+ 1 > N)
t++ ;
if ( h- t+ 1 == N)
ans= min ( ans, q[ h] - q[ t] + 1 ) ;
}
return ans;
}
void update ( int & x, int y)
{
x= min ( x, y) ;
}
int main ( )
{
int T;
scanf ( "%d" , & T) ;
while ( T-- )
{
scanf ( "%d%d%d" , & n, & m, & k) ;
int ans= inf;
for ( int i= 0 ; i<= n; i++ )
for ( int j= 0 ; j<= k; j++ )
d[ i] [ j] = inf;
d[ 0 ] [ 0 ] = 0 ;
for ( int cur= 1 ; cur<= n; cur++ )
{
scanf ( "%s" , s+ 1 ) ;
int N= 0 ;
for ( int i= 1 ; i<= m; i++ )
N+ = s[ i] - '0' ;
for ( int i= 0 ; i<= N; i++ )
{
if ( N- i> k) continue ;
int tmp= gao ( i) ;
for ( int j= 0 ; j+ N- i<= k; j++ )
update ( d[ cur] [ j+ N- i] , d[ cur- 1 ] [ j] + tmp) ;
}
}
for ( int i= 0 ; i<= k; i++ )
update ( ans, d[ n] [ i] ) ;
printf ( "%d\n" , ans) ;
}
}