海亮 7.8 周赛
\(100+100+50+45+1=296pts\)
被薄纱 乐 歇斯底里地打完部分分 发现别人已经打完了正解 我不好说()
#A. 摧毁遗迹
做法显然 排序后取最大值和次大值轮流攻击即可
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , k , cnt , st , a[N];
signed main ()
{
n = read() , k = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
sort ( a + 1 , a + n + 1 , [](const int a , const int b) { return a > b; } );
int tmp[2] = { a[1] , a[2] };
while ( k > 0 )
{
k -= tmp[st];
st ^= 1;
cnt ++;
}
printf ( "%d" , cnt );
return 0;
}
#B. 区间游戏
观察样例可得做法:
按照区间大小从小到大排序 那么从最小的区间开始打标记 这个区间里没有被标记的数就是答案 再向上覆盖区间 因为小区间一定在大区间之前被覆盖 所以正确性是可以保证的
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , k , vis[N];
struct node { int l , r , len , id , ans; } a[N];
signed main ()
{
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i].l = read() , a[i].r = read() , a[i].len = a[i].r - a[i].l + 1 , a[i].id = i;
sort ( a + 1 , a + n + 1 , [](const node &a , const node &b) { return a.len < b.len; } );
for ( int i = 1 ; i <= n ; i ++ )
for ( int j = a[i].l ; j <= a[i].r ; j ++ )
if ( !vis[j] ) a[i].ans = j , vis[j] = 1;
sort ( a + 1 , a + n + 1 , [](const node &a , const node &b) { return a.id < b.id; } );
for ( int i = 1 ; i <= n ; i ++ )
printf ( "%d %d %d\n" , a[i].l , a[i].r , a[i].ans );
return 0;
}
#C. 前方之风
考场上想了一个假二分 打完后发现样例2没有单调性 寄
正解是离线+双指针移动 我们发现\(k\)具有单调性 即\(k\)大的询问 覆盖区间一定比\(k\)小的询问大
#include <bits/stdc++.h>
using namespace std;
#define mid ((l+r)/2)
#define int long long
const int N = 1e6 + 5;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , q , a[N] , sum[N];
struct query { int val , id , res; } k[N];
void solve()
{
for ( int i = 1 , l = 1 ; l <= n ; l ++ )
{
while ( sum[l] - k[i].val * ( n - l + 1 ) <= a[l] * ( n - l + 1 ) && i <= q ) k[i].res = n - l + 1 , i ++;
}
}
signed main ()
{
int T = read();
while ( T -- )
{
memset ( sum , 0 , sizeof sum );
memset ( k , 0 , sizeof k );
n = read() , q = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i <= q ; i ++ ) k[i].val = read() , k[i].id = i;
sort ( a + 1 , a + n + 1 );
sort ( k + 1 , k + q + 1 , [](const query &a , const query &b) { return a.val > b.val; } );
for ( int i = n ; i ; i -- ) sum[i] = sum[i+1] + a[i];
solve();
sort ( k + 1 , k + q + 1 , [](const query &a , const query &b) { return a.id < b.id; } );
for ( int i = 1 ; i <= q ; i ++ ) printf ( "%lld " , k[i].res );
puts("");
}
return 0;
}
P8971 『GROI-R1』 虹色的彼岸花
对于一个全部都是有权值边的树 你定了根的权值 那么这棵子树的所有点的取值都确定了
那么对于无权边 就将图分成了森林 我们对于每一个森林处理即可(通过打标记dfs实现)
最终达到的目标就是求出一个满足全部点的范围 然后将所有森林的取值范围都相乘即可
那么我们设一个解为\(x+k\)的形式 设置\(k=val[x]\) 那么对于一个子节点\(y\) 连边为\(e[i].w\) 可以得到\(l\le x + val[x] \le r\)
那么我们\(y=e[i].w-(x+val[x])=e[i].w-x-val[x]\)
那么此时更新系数\(val[y]=e[i].w-val[x]\) 带入可得\(y=-x+val[y]\) 那么\(x=val[y]-y\)
我们用这个东西来更新\(x\)的取值范围 \(y\)的取值范围就是\(l,r\) 那么我们得到此时\(val[y]-r\le x\le val[y]-l\) 进而可以更新最小的\(x\)取值范围上下界
这是\(x\)前系数为正的情况 那么对于这个\(y\) 前面系数就要和父亲相反 也是类似的情况 此时将上柿中的\(x\)改为\(-x\) 即可得出\(l-val[y]\le x\le r-val[y]\)
所以我们开两个数组 \(val\)和\(typ\) 维护的是该点后面的系数和该点前面的系数\((0/1)\)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;
const int mod = 1e9 + 7;
inline int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , a[N] , vis[N], nowl , nowr , typ[N] , val[N] , l , r , ans;
int head[N] , cnt;
struct node { int to , nxt , w; } e[N];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w } , head[u] = cnt; }
void dfs ( int u )
{
vis[u] = 1;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( vis[v] ) continue;
typ[v] = typ[u] ^ 1;
val[v] = e[i].w - val[u];
if ( typ[u] ) nowl = max ( nowl , val[v] - r ) , nowr = min ( nowr , val[v] - l );
else nowl = max ( nowl , l - val[v] ) , nowr = min ( nowr , r - val[v] );
dfs(v);
}
}
signed main ()
{
int T = read();
while ( T -- )
{
ans = 1 , cnt = 0;
memset ( head , 0 , sizeof head );
memset ( vis , 0 , sizeof vis );
memset ( val , 0 , sizeof val );
memset ( typ , 0 , sizeof typ );
n = read() , l = read() , r = read();
for ( int i = 1 ; i < n ; i ++ )
{
int op = read() , u = read() , v = read() , w;
if ( op ) w = read() , add ( u , v , w ) , add ( v , u , w );
}
for ( int i = 1 ; i <= n ; i ++ )
{
if ( vis[i] ) continue;
nowl = l , nowr = r;
typ[i] = 1;
dfs(i);
if ( nowl > nowr ) ans = 0;
else ans = ans * ( nowr - nowl + 1 ) % mod;
}
printf ( "%lld\n" , ans );
}
return 0;
}
P6748 『MdOI R3』Fallen Lord
设\(deg[x]\)表示\(x\)点的度数 那么题目中就是要让每个点的连边中最少有\(\lfloor \frac {deg[x]} {2}\rfloor+1\) 条边小于等于\(a[x]\)
也就是最多有\(\lceil\frac {deg[x]}{2} \rceil-1\)条边大于等于\(a[x]\)
我们设置\(dp[x][0/1]\)表示点\(x\)的答案
其中\(dp[x][0]\)表示\(x\)连向父亲的边权值小于等于\(a[x]\)时的答案
而\(dp[x][1]\)表示这个权值大于\(a[x]\)时 \(x\)子树内最大的边权和
考虑转移
\(dp[x][0]\)
考虑\(x\)的每个儿子\(y\) 设\(p\)为\(x->y\)的边权小于等于\(a[x]\)的时候 \(y\)子树内的边权和+\(x->y\)的边权和 \(q\)为大于\(a[x]\)的情况
\(p=max(dp[y][0]+min(a[x],a[y]),dp[y][1]+a[x])\)
解释:如果\(y\)连向父亲的权值小于等于\(a[y]\) 那么取满足条件的\(a[x]\)和\(a[y]\)最小值即可
否则边权只能为\(a[x]\)
\(q=max(dp[y][0]+a[y],dp[y][1]+m)\)
解释:如果\(y\)连向父亲的权值小于等于\(a[y]\) 那么取符合条件的\(a[y]\)即可(还是要满足小于等于\(a[y]\)的条件)
否则直接给上界权值\(m\)
那么我们求出来所有的\(q\)和\(p\) 不难发现 \(dp[i][0]\)的情况可以取得\(now[i]\)个 否则对于\(dp[i][1]\)的情况可以取得\(now[i]-1\)个
对每一个\(q-p\)排序 \(f[i]\)为前缀和 那么对于\(dp[i][0]\)的最终答案就是子树的所有\(p\) 加上\(f[now[i]]\)
树形\(dp\)即可
一个细节:如果我们的\(now[i]<=1\) 那么不可能通过儿子产生贡献 记为\(-inf\)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define int long long
const int N = 5e5 + 5;
const int inf = 9e18;
inline int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int n , m , a[N] , b[N] , dag[N] , sum[N] , f[N][2] , num[N];//我们开一个t[i]数组实时维护它和当前len的差
int head[N] , cnt;
struct node { int to , nxt; } e[N<<2];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }
void dfs ( int u , int fa )
{
int ss = 0 , tot = 0;
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( v == fa ) continue;
dfs ( v , u );
}
for ( int i = head[u] ; i ; i = e[i].nxt )
{
int v = e[i].to;
if ( v == fa ) continue;
int p = max ( f[v][0] + min ( a[u] , a[v] ) , f[v][1] + a[u] );
int q = max ( f[v][0] + a[v] , f[v][1] + m );
ss += p;
b[++tot] = q - p;
}
sort ( b + 1 , b + 1 + tot , greater<int>() );
for ( int i = 1 ; i <= tot ; i ++ ) sum[i] = sum[i-1] + b[i];
f[u][0] = ss + sum[num[u]] , f[u][1] = ( num[u] >= 1 ) ? ss + sum[num[u]-1]: -inf;
}
char ch;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i < n ; i ++ )
{
int u = read() , v = read();
add ( u , v ) , add ( v , u );
dag[u] ++ , dag[v] ++;
}
for ( int i = 1 ; i <= n ; i ++ ) num[i] = ( dag[i] + 1 ) / 2 - 1;
dfs ( 1 , 0 );
cout << max ( f[1][0] , f[1][1] ) << endl;
return 0;
}