文章目录
NOMURA Programming Contest 2021(AtCoder Regular Contest 121)
A - 2nd Greatest Distance
大模拟讨论yyds
将点按 x , y x,y x,y分别排序
-
x x x贡献最大值的点对等于 y y y贡献最大值的点对
次小值就变成 x / y x/y x/y中最大次小和次大最小一共四种组合中的最大值
-
x x x最大最小贡献点对不等于 y y y最大最小点对
-
x x x最大最小更大
y y y最大最小与 x x x中次大最小和最大次小比较
-
y y y最大最小更大
x x x最大最小与 y y y中次大最小和最大次小比较
-
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 200005
struct node {
int x, y, id;
}h1[maxn], h2[maxn];
int n;
bool cmp1( node s, node t ) {
return s.x < t.x;
}
bool cmp2( node s, node t ) {
return s.y < t.y;
}
signed main() {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ ) {
scanf( "%lld %lld", &h1[i].x, &h1[i].y );
h1[i].id = i;
h2[i] = h1[i];
}
sort( h1 + 1, h1 + n + 1, cmp1 );
sort( h2 + 1, h2 + n + 1, cmp2 );
if( ( h1[1].id == h2[1].id && h1[n].id == h2[n].id ) || ( h1[1].id == h2[n].id && h1[n].id == h2[1].id ) )
printf( "%lld\n", max( max( h1[n].x - h1[2].x, h1[n - 1].x - h1[1].x ), max( h2[n].y - h2[2].y, h2[n - 1].y - h2[1].y ) ) );
else if( h1[n].x - h1[1].x < h2[n].y - h2[1].y )
printf( "%lld\n", max( h1[n].x - h1[1].x, max( h2[n].y - h2[2].y, h2[n - 1].y - h2[1].y ) ) );
else
printf( "%lld\n", max( h2[n].y - h2[1].y, max( h1[n].x - h1[2].x, h1[n - 1].x - h1[1].x ) ) );
return 0;
}
B - RGB Matching
显然,最后要么三种颜色的狗全是偶数条,两两配对,答案为 0 0 0;要么只会恰有两种颜色的狗为奇数条
case 1
两种颜色中选择不满意值相差最小的两条狗
具体来说,枚举一种颜色的狗,再二分左右求出与该狗不满意值差距最小的狗,整体不满意值取最小值
case 2
第三颜色狗起一个中转点的作用
具体来说,分别枚举两种颜色的狗,以及在第三颜色中求出与枚举狗不满意值差距最小的狗
整体取不满意值最小,两种颜色最小再相加
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define int long long
vector < int > G[3];
int n, pos_l, pos_r;
int id( char ch ) {
if( ch == 'R' ) return 0;
if( ch == 'B' ) return 1;
if( ch == 'G' ) return 2;
}
int Fabs( int x ) {
return x < 0 ? -x : x;
}
void work( int c, int val ) {
int l = 0, r = G[c].size() - 1;
pos_l = 0, pos_r = G[c].size() - 1;
while( l <= r ) {
int mid = ( l + r ) >> 1;
if( G[c][mid] <= val ) pos_l = mid, l = mid + 1;
else r = mid - 1;
}
l = 0, r = G[c].size() - 1;
while( l <= r ) {
int mid = ( l + r ) >> 1;
if( G[c][mid] >= val ) pos_r = mid, r = mid - 1;
else l = mid + 1;
}
}
signed main() {
scanf( "%lld", &n );
for( int i = 1;i <= ( n << 1 );i ++ ) {
char c; int a;
scanf( "%lld %c", &a, &c );
G[id( c )].push_back( a );
}
int c1 = -1, c2 = -1;
for( int i = 0;i < 3;i ++ )
if( G[i].size() & 1 ) {
if( ~ c1 ) c2 = i;
else c1 = i;
}
if( c1 == -1 ) return ! printf( "0\n" );
else {
int ans = 1e18;
sort( G[c2].begin(), G[c2].end() );
for( int i = 0;i < G[c1].size();i ++ ) {
work( c2, G[c1][i] );
ans = min( ans, min( Fabs( G[c1][i] - G[c2][pos_l] ), Fabs( G[c2][pos_r] - G[c1][i] ) ) );
}
int ans1 = 1e18, ans2 = 1e18, c;
for( int i = 0;i < 3;i ++ )
if( c1 == i || c2 == i ) continue;
else c = i;
if( ! G[c].size() ) goto pass;
sort( G[c].begin(), G[c].end() );
for( int i = 0;i < G[c1].size();i ++ ) {
work( c, G[c1][i] );
ans1 = min( ans1, min( Fabs( G[c1][i] - G[c][pos_l] ), Fabs( G[c][pos_r] - G[c1][i] ) ) );
}
for( int i = 0;i < G[c2].size();i ++ ) {
work( c, G[c2][i] );
ans2 = min( ans2, min( Fabs( G[c2][i] - G[c][pos_l] ), Fabs( G[c][pos_r] - G[c2][i] ) ) );
}
pass : printf( "%lld\n", min( ans, ans1 + ans2 ) );
}
return 0;
}
C - Odd Even Sort
题读错做法千奇百怪错 奇数次操作只能操作奇数位置,偶数次操作只能操作偶数位置
问题不要求操作最小,只求不超过 n 2 n^2 n2(冒泡排序复杂度),想法一下子就来了,明显的构造
从最大值往最小值依次考虑( k k k),也就是只用考虑右移的操作
不难发现,如果 k k k本身处于位置与操作次数恰好同奇偶,那么可以直接一直操作 k k k直到位置 k k k
如果不同奇偶,那么就像能否先操作一次无关的位置,再一直操作 k k k
这显然是可以的,每次奇偶就让 1 , 2 1,2 1,2位置与后面一个进行交换
当然还有位置与值已经匹配的,直接下一个哈
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 505
int T, n;
int a[maxn], b[maxn], ans[maxn * maxn];
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d", &n );
for( int i = 1;i <= n;i ++ )
scanf( "%d", &a[i] ), b[i] = a[i];
sort( b + 1, b + n + 1 );
int cnt = 0, ip = n;
for( int t = 1;t <= n * n && ip;t ++ ) {
bool flag = 0;
for( int i = 1;i < n;i ++ )
if( a[i] > a[i + 1] ) {
flag = 1;
break;
}
if( ! flag ) break;
int pos;
for( int i = 1;i <= n;i ++ )
if( a[i] == b[ip] ) {
pos = i;
break;
}
if( ( pos & 1 ) == ( t & 1 ) ) {
while( pos < ip ) {
ans[++ cnt] = pos;
t ++;
swap( a[pos], a[pos + 1] );
pos ++;
}
t --;
ip --;
}
else {
flag = 1;
for( int i = ( ( t & 1 ) ? 1 : 2 );i < n;i += 2 )
if( a[i] > a[i + 1] ) {
ans[++ cnt] = i;
swap( a[i], a[i + 1] );
flag = 0;
break;
}
if( flag ) {
int x = ( ( t & 1 ) ? 1 : 2 );
ans[++ cnt] = x;
swap( a[x], a[x + 1] );
}
}
}
printf( "%d\n", cnt );
if( cnt ) {
for( int i = 1;i <= cnt;i ++ )
printf( "%d ", ans[i] );
printf( "\n" );
}
}
return 0;
}
D - 1 or 2
肯定是最大最小,次大次小…一一配对
a < b < c < d ⇒ m a x ( a + d , b + c ) ≤ m a x ( a + c , b + d ) ; m i n ( a + c , b + d ) ≤ m i n ( a + d , b + c ) a<b<c<d\Rightarrow max(a+d,b+c)\le max(a+c,b+d);min(a+c,b+d)\le min(a+d,b+c) a<b<c<d⇒max(a+d,b+c)≤max(a+c,b+d);min(a+c,b+d)≤min(a+d,b+c)
如果是一个蛋糕,不妨看做和 0 0 0合并,所以只需要枚举 0 0 0的个数排序顺次合并即可, O ( n 2 ) O(n^2) O(n2)
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 5005
#define int long long
vector < int > g;
int n;
signed main() {
scanf( "%lld", &n );
for( int i = 1, x;i <= n;i ++ ) {
scanf( "%lld", &x );
g.push_back( x );
}
sort( g.begin(), g.end() );
int Size = g.size(), maxx = -1e18, minn = 1e18;
for( int i = 0;i < ( Size >> 1 );i ++ ) {
maxx = max( g[Size - 1 - i] + g[i], maxx );
minn = min( g[Size - 1 - i] + g[i], minn );
}
if( Size & 1 ) {
maxx = max( g[Size >> 1], maxx );
minn = min( g[Size >> 1], minn );
}
int ans = maxx - minn;
for( int i = 1;i <= n;i ++ ) {
g.push_back( 0 ), Size ++;
for( int i = Size - 1;i;i -- )
if( g[i] < g[i - 1] ) swap( g[i], g[i - 1] );
else break;
maxx = -1e18, minn = 1e18;
for( int i = 0;i < ( Size >> 1 );i ++ ) {
maxx = max( g[Size - 1 - i] + g[i], maxx );
minn = min( g[Size - 1 - i] + g[i], minn );
}
if( Size & 1 ) {
maxx = max( g[Size >> 1], maxx );
minn = min( g[Size >> 1], minn );
}
ans = min( ans, maxx - minn );
}
printf( "%lld\n", ans );
return 0;
}
E - Directed Tree
套路都见过,却没有反应过来
设 d p i , j : i dp_{i,j}:i dpi,j:i 子树内不满足条件的节点数为 j j j
d p u , j + k = ∑ j = 0 s i z e u ∑ k = 0 s i z e v d p u , j × d p v , k dp_{u,j+k}=\sum_{j=0}^{size_u}\sum_{k=0}^{size_v}dp_{u,j}\times dp_{v,k} dpu,j+k=∑j=0sizeu∑k=0sizevdpu,j×dpv,k
最后容斥
a n s = ∑ i = 0 n ( − 1 ) i d p 1 , i × ( n − i ) ! ans=\sum_{i=0}^n(-1)^idp_{1,i}\times (n-i)! ans=∑i=0n(−1)idp1,i×(n−i)!
#include <cstdio>
#include <vector>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 2005
vector < int > G[maxn];
int n;
int fac[maxn], siz[maxn], g[maxn];
int dp[maxn][maxn];
void dfs( int u ) {
dp[u][0] = 1;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
dfs( v );
for( int j = 0;j <= siz[u];j ++ )
for( int k = 0;k <= siz[v];k ++ )
g[j + k] = ( g[j + k] + dp[u][j] * dp[v][k] % mod ) % mod;
siz[u] += siz[v];
for( int j = 0;j <= siz[u];j ++ )
dp[u][j] = g[j], g[j] = 0;
}
for( int i = siz[u];~ i;i -- )
dp[u][i + 1] = ( dp[u][i + 1] + dp[u][i] * ( siz[u] - i ) % mod ) % mod;
siz[u] ++;
}
signed main() {
scanf( "%lld", &n );
for( int i = 2, p;i <= n;i ++ ) {
scanf( "%lld", &p );
G[p].push_back( i );
}
dfs( 1 );
fac[0] = 1;
for( int i = 1;i <= n;i ++ )
fac[i] = fac[i - 1] * i % mod;
int ans = 0;
for( int i = 0;i <= n;i ++ )
if( i & 1 ) ans = ( ans - dp[1][i] * fac[n - i] % mod + mod ) % mod;
else ans = ( ans + dp[1][i] * fac[n - i] % mod ) % mod;
printf( "%lld\n", ans );
return 0;
}
F - Logical Operations on Tree
N = 1 N=1 N=1结果显然,考虑其他情况
如果一个叶子标记为1
并且与之相连的边是OR
不管其它长什么样都可以将这条边的操作放在最后一步,从而符合要求,简称好树
接下来考虑没有这种叶子,合并出新点的情况
-
如果叶子
u
的标记为0
,且相连边是AND
,则这条边操作后,新点一定标记为0
如果执行这条边后的树是好树,那么原来的树也一定是好树
-
如果叶子
u
的标记为0
,且相连边是OR
,则这条边操作后,原来与叶子相连点的标记即为新点标记如果执行这条边后的树是好树,那么原来的树也一定是好树
-
如果叶子
u
的标记为1
,且相连边是AND
,则这条边操作后,原来与叶子相连点的标记即为新点标记如果执行这条边后的树是好树,那么原来的树也一定是好树
在树上从叶子到父亲搭建,出现1 OR
时剩下的点边不管是什么,一定也是好树了;否则把叶子扔掉,父亲节点可能为0/1
,在树上
D
P
DP
DP
f
:
f:
f: 子树内没有出现1 OR
想要边的树个数,$g: $ 在
f
f
f所有方案数中好树的个数
ps:没有出现1 OR
这种关键叶子标记1
指向父亲种类,但是可以出现1 OR
父亲标记1
的情况,因为可能父亲的其它儿子操作时会导致父亲标记变化
#include <cstdio>
#include <vector>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 100005
vector < int > G[maxn];
int n;
int f[maxn], g[maxn];
int qkpow( int x, int y ) {
int ans = 1;
while( y ) {
if( y & 1 ) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
void dfs( int u, int fa ) {
f[u] = 2, g[u] = 1;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if( v == fa ) continue;
else dfs( v, u );
f[u] = ( ( f[v] * 2 - g[v] ) * f[u] % mod + mod ) % mod;
g[u] = g[u] * f[v] % mod;
}
}
signed main() {
scanf( "%lld", &n );
for( int i = 1, u, v;i < n;i ++ ) {
scanf( "%lld %lld", &u, &v );
G[u].push_back( v );
G[v].push_back( u );
}
dfs( 1, 0 );
printf( "%lld\n", ( qkpow( 2, n * 2 - 1 ) - f[1] + g[1] + mod ) % mod );
return 0;
}