文章目录
#703 (Div. 2)
A. Shifting Stacks
从左往右构造递增0,1,2...
,如果这样都不能递增就肯定无解
雷区:不能用等差数列算个数,因为这个移动必须是左到右的不能逆,hack: 0 0 3 AC:NO WA:YES
B. Eastern Exhibition
以前考试遇到过的相似度百分之九十五,横纵坐标拆开分别取中位数即可;偶数个点中位数是一段区间
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 1005
int T, n;
int x[maxn], y[maxn];
signed main() {
scanf( "%lld", &T );
while( T -- ) {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ )
scanf( "%lld %lld", &x[i], &y[i] );
sort( x + 1, x + n + 1 );
sort( y + 1, y + n + 1 );
if( n & 1 ) printf( "1\n" );
else {
int l = n >> 1, r = l + 1;
printf( "%lld\n", ( x[r] - x[l] + 1 ) * ( y[r] - y[l] + 1 ) );
}
}
return 0;
}
C. Guessing the Greatest
第一次询问整个序列确定次大值,然后再一次询问判断最大值是在左边还是右边
之后就是二分位置与次大值进行询问,找到最大值
煞笔二分再次死亡
#include <cstdio>
int n;
int print( int l, int r ) {
if( l >= r ) return -1;
printf( "? %d %d\n", l, r );
fflush( stdout );
int pos;
scanf( "%d", &pos );
return pos;
}
int main() {
scanf( "%d", &n );
int pos = print( 1, n );
if( pos > 1 && print( 1, pos ) == pos ) {
int l = 1, r = pos;
while( l < r ) {
int mid = ( l + r + 1 ) >> 1;
if( print( mid, pos ) == pos ) l = mid;
else r = mid - 1;
}
printf( "! %d\n", l );
fflush( stdout );
}
else {
int l = pos, r = n;
while( l < r ) {
int mid = ( l + r ) >> 1;
if( print( pos, mid ) == pos ) r = mid;
else l = mid + 1;
}
printf( "! %d\n", l );
fflush( stdout );
}
return 0;
}
D. Max Median
二分最后的答案,将原数组转换为大于等于为 1 1 1,小于为 − 1 -1 −1,看有无一段长度大于等于 k k k的区间和为正
对于第 i i i个位置,有 0 , 1 , 2 , . . . , i − k 0,1,2,...,i-k 0,1,2,...,i−k种选择,用前缀和最小值优化即可做到 O ( n l o g n ) O(nlogn) O(nlogn)
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 200005
int n, k, ans;
int a[maxn], b[maxn], minn[maxn];
bool check( int x ) {
for( int i = 1;i <= n;i ++ )
if( a[i] >= x ) b[i] = 1;
else b[i] = -1;
for( int i = 1;i <= n;i ++ )
b[i] += b[i - 1];
for( int i = 1;i <= n;i ++ )
minn[i] = min( minn[i - 1], b[i] );
for( int i = k;i <= n;i ++ )
if( b[i] > minn[i - k] ) return 1;
return 0;
}
int main() {
scanf( "%d %d", &n, &k );
for( int i = 1;i <= n;i ++ )
scanf( "%d", &a[i] );
int l = 1, r = n;
while( l <= r ) {
int mid = ( l + r ) >> 1;
if( check( mid ) ) ans = mid, l = mid + 1;
else r = mid - 1;
}
printf( "%d\n", ans );
return 0;
}
E. Paired Payment
如果可以一条一条边地走,就是一个dijkstra板子
但必须两条两条走,也就是说这一次走的边产生的贡献与上一条边边权挂钩
再观察边权范围,只有 50 50 50,小得离谱;很有可能就是从这里入手
不妨把上一条边权是多少带着dijkstra
设 d i s i , j dis_{i,j} disi,j表示走到 i i i点上一条的边权为 j j j的最短路
最后为什么是 d i s i , 0 dis_{i,0} disi,0,这是为了dijkstra的正确性, d i s i , 0 dis_{i,0} disi,0含义比较特殊,表示 i i i为结束点(第二条边的入点)
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define int long long
#define maxn 200005
struct node {
int u, lst, d;
node(){}
node( int U, int Lst, int D ) {
u = U, lst = Lst, d = D;
}
bool operator < ( node t ) const {
return d > t.d;
}
};
int n, m;
priority_queue < node > q;
vector < pair < int, int > > G[maxn];
int dis[maxn][55];
signed main() {
scanf( "%lld %lld", &n, &m );
for( int i = 1, u, v, w;i <= m;i ++ ) {
scanf( "%lld %lld %lld", &u, &v, &w );
G[u].push_back( make_pair( v, w ) );
G[v].push_back( make_pair( u, w ) );
}
memset( dis, 0x3f, sizeof( dis ) );
dis[1][0] = 0;
q.push( node( 1, 0, 0 ) );
while( ! q.empty() ) {
node t = q.top(); q.pop();
int u = t.u, lst = t.lst;
if( dis[u][lst] < t.d ) continue;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i].first, w = G[u][i].second;
int nxt = lst ? 0 : w;
if( dis[v][nxt] > dis[u][lst] + 2 * lst * w + w * w )
q.push( node( v, nxt, dis[v][nxt] = dis[u][lst] + 2 * lst * w + w * w ) );
}
}
for( int i = 1;i <= n;i ++ )
if( dis[i][0] >= 0x3f3f3f3f ) printf( "-1 " );
else printf( "%lld ", dis[i][0] );
return 0;
}
F. Pairs of Paths
事实上,只有两种情况
case 1:
两条路径相交于一条路径的
l
c
a
lca
lca
case 2:
两条路径拥有公共的
l
c
a
lca
lca
这意味着,符合条件的答案一定是交于某个 l c a lca lca的
对于每一条路径都定义一个三元组 ( a , b , l c a ) (a,b,lca) (a,b,lca),其中 a , b a,b a,b分别表示两个端点的树根,如果端点是 l c a lca lca,那么重新给个编号(因为这时候要判定为不同)
case1:
按
l
c
a
lca
lca排序,
a
a
a严格递减排序后,用桶存储之前信息,然后每次加
S
−
c
n
t
b
S-cnt_b
S−cntb,然后再把
c
n
t
a
+
+
,
c
n
t
b
+
+
cnt_a++,cnt_b++
cnta++,cntb++
case2:
另一个
l
c
a
lca
lca一定是这个交点
l
c
a
lca
lca的祖先,用树状数组记录
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 600005
#define int long long
struct node{
int u, v, tu, tv, lca;
}MS[maxn];
vector < int > G[maxn];
int n, m, T, cnt;
int dep[maxn], l[maxn], r[maxn], tree[maxn], tot[maxn];
int f[maxn][20];
bool cmp( node x, node y ) {
if( dep[x.lca] == dep[y.lca] )
if( x.lca == y.lca ) return x.tu > y.tu;
else return x.lca < y.lca;
else
return dep[x.lca] < dep[y.lca];
}
void dfs( int u, int fa ) {
dep[u] = dep[fa] + 1, f[u][0] = fa;
for( int i = 1;i < 20;i ++ )
f[u][i] = f[f[u][i - 1]][i - 1];
l[u] = ++ cnt;
for( int i = 0;i < G[u].size();i ++ ) {
int v = G[u][i];
if( v == fa ) continue;
else dfs( v, u );
}
r[u] = cnt;
}
int lca( int u, int v ) {
if( dep[u] < dep[v] ) swap( u, v );
for( int i = 19;~ i;i -- )
if( dep[f[u][i]] >= dep[v] )
u = f[u][i];
if( u == v ) return u;
for( int i = 19;~ i;i -- )
if( f[u][i] != f[v][i] )
u = f[u][i], v = f[v][i];
return f[u][0];
}
int get_top( int u, int lca ) {
if( u == lca ) return ++ T;
for( int i = 19;~ i;i -- )
if( dep[f[u][i]] > dep[lca] )
u = f[u][i];
return u;
}
int lowbit( int x ) {
return x & ( -x );
}
void add( int x ) {
for( int i = x;i <= n;i += lowbit( i ) )
tree[i] ++;
}
int ask( int x ) {
int ans = 0;
for( int i = x;i;i -= lowbit( i ) )
ans += tree[i];
return ans;
}
signed main() {
scanf( "%lld", &n ); T = 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 );
scanf( "%lld", &m );
for( int i = 1;i <= m;i ++ ) {
scanf( "%lld %lld", &MS[i].u, &MS[i].v );
MS[i].lca = lca( MS[i].u, MS[i].v );
MS[i].tu = get_top( MS[i].u, MS[i].lca );
MS[i].tv = get_top( MS[i].v, MS[i].lca );
if( MS[i].tu > MS[i].tv ) {
swap( MS[i].tu, MS[i].tv );
swap( MS[i].u, MS[i].v );
}
}
sort( MS + 1, MS + m + 1, cmp );
int ans = 0;
for( int i = 1, j;i <= m;i = j + 1 ) {
j = i;
while( j < m && MS[j + 1].lca == MS[i].lca ) j ++;
cnt = 0;
for( int s = i, t;s <= j;s = t + 1 ) {
t = s;
while( t < j && MS[t + 1].tu == MS[s].tu ) t ++;
for( int k = s;k <= t;k ++ )
ans += cnt - tot[MS[k].tv];
for( int k = s;k <= t;k ++ )
tot[MS[k].tv] ++, tot[MS[k].tu] ++;
cnt += ( t - s + 1 );
}
for( int k = i;k <= j;k ++ )
tot[MS[k].tu] = tot[MS[k].tv] = 0;
for( int k = i;k <= j;k ++ ) {
ans += ask( r[MS[k].lca] ) - ask( l[MS[k].lca] - 1 );
if( MS[k].tu <= n ) ans -= ask( r[MS[k].tu] ) - ask( l[MS[k].tu] - 1 );
if( MS[k].tv <= n )ans -= ask( r[MS[k].tv] ) - ask( l[MS[k].tv] - 1 );
}
for( int k = i;k <= j;k ++ )
add( l[MS[k].u] ), add( l[MS[k].v] );
}
printf( "%lld\n", ans );
return 0;
}