#709 (Div. 2)
A. Prison Break
就是每个监狱破一扇门,输出 a × b a\times b a×b即可
B. Restore Modulo
就是取模意义下的操作,分大小操作,随便确定两个差值,然后构造即可
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 100005
int T, n;
int a[maxn];
signed main() {
scanf( "%lld", &T );
while( T -- ) {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &a[i] );
if( n == 1 ) {
printf( "0\n" );
continue;
}
int d1 = -1, d2 = -1;
bool flag = 0;
for( int i = 1;i < n;i ++ )
if( a[i + 1] > a[i] )
if( ~ d1 )
if( a[i + 1] - a[i] != d1 ) {
flag = 1;
break;
} else;
else d1 = a[i + 1] - a[i];
else
if( ~ d2 )
if( a[i] - a[i + 1] != d2 ) {
flag = 1;
break;
} else;
else d2 = a[i] - a[i + 1];
if( flag ) printf( "-1\n" );
else if( d1 == -1 || d2 == -1 ) printf( "0\n" );
else {
int maxx = 0;
for( int i = 1;i <= n;i ++ ) maxx = max( maxx, a[i] );
if( d1 + d2 <= maxx ) printf( "-1\n" );
else printf( "%lld %lld\n", d1 + d2, ( a[2] + d1 + d2 - a[1] ) % ( d1 + d2 ) );
}
}
return 0;
}
C. Basic Diplomacy
贪心,对于某天有多种选择的肯定优先满足限制比较死的,比如某天就必须和某人玩
发现,只要 k = 1 k=1 k=1的条件满足,后面只要使用次数没超过一半就可以跟那个人玩
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
#define maxn 100005
vector < int > G[maxn];
int T, n, m;
int cnt[maxn], ans[maxn], used[maxn];
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d %d", &n, &m );
for( int i = 1;i <= n;i ++ ) cnt[i] = used[i] = 0;
for( int i = 1;i <= m;i ++ ) G[i].clear();
for( int i = 1, k;i <= m;i ++ ) {
scanf( "%d", &k );
for( int j = 1, x;j <= k;j ++ ) {
scanf( "%d", &x );
G[i].push_back( x );
cnt[x] ++;
}
if( k == 1 ) ans[i] = G[i][0], used[G[i][0]] ++;
}
int lim = ( m + 1 ) >> 1;
for( int i = 1;i <= m;i ++ )
if( G[i].size() == 1 ) continue;
else {
for( int j = 0;j < G[i].size();j ++ )
if( used[G[i][j]] >= lim ) continue;
else {
ans[i] = G[i][j];
used[G[i][j]] ++;
break;
}
}
bool flag = 0;
for( int i = 1;i <= n;i ++ )
if( used[i] > lim ) {
flag = 1;
break;
}
if( flag ) printf( "NO\n" );
else {
printf( "YES\n" );
for( int i = 1;i <= m;i ++ )
printf( "%d ", ans[i] );
printf( "\n" );
}
}
return 0;
}
D. Playlist
想复杂了——就是分类大讨论
可以找两个质数,例如 1 e 9 + 9 1e9+9 1e9+9,遇到不互质的时候就赋质数
#include <cstdio>
#include <queue>
using namespace std;
#define maxn 100005
queue < int > q, ans;
int T, n;
int a[maxn], pos[maxn];
bool vis[maxn];
int gcd( int x, int y ) {
if( ! y ) return x;
else return gcd( y, x % y );
}
int main() {
scanf( "%d", &T );
while( T -- ) {
scanf( "%d", &n );
for( int i = 1;i <= n;i ++ ) vis[i] = pos[i] = 0;
for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );
for( int i = 1;i < n;i ++ )
if( gcd( a[i], a[i + 1] ) == 1 )
ans.push( i + 1 ), vis[i + 1] = 1, q.push( i ), pos[i] = i + 1, i ++;
if( ! vis[n] && gcd( a[n], a[1] ) == 1 )
ans.push( 1 ), vis[1] = 1, q.push( n ), pos[n] = 2;
while( ! q.empty() ) {
int t = q.front(); q.pop();
if( vis[t] ) continue;
if( pos[t] == n + 1 ) pos[t] = 1;
while( vis[pos[t]] )
pos[t] = pos[t] == n ? 1 : pos[t] + 1;
if( gcd( a[pos[t]], a[t] ) == 1 )
ans.push( pos[t] ), vis[pos[t]] = 1, q.push( t ), pos[t] ++;
}
printf( "%d ", ans.size() );
while( ! ans.empty() ) printf( "%d ", ans.front() ), ans.pop();
printf( "\n" );
}
return 0;
}
E. Skyline Photo
普通的 D P DP DP式子: d p i = d p j + w ( j + 1 , i ) dp_i=dp_j+w(j+1,i) dpi=dpj+w(j+1,i)
w ( j + 1 , i ) w(j+1,i) w(j+1,i)事实上是单调栈更新到 i i i时,栈里面第一个下标大于 j j j的房子的美丽度
对于栈里面的第 i i i个元素,管辖着区间 ( s i − 1 , s i ] (s_{i-1},s_i] (si−1,si],维护的是递增的单调栈
f i = m a x k = 1 t o p ( m a x j = s k − 1 + 1 s k f j + b s k ) f_i=max_{k=1}^{top}(max_{j=s_{k-1}+1}^{s_k}f_j+b_{s_k}) fi=maxk=1top(maxj=sk−1+1skfj+bsk)
#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 300005
int n, top;
int h[maxn], b[maxn], s[maxn], g[maxn], f[maxn], dp[maxn];
signed main() {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &h[i] );
for( int i = 1;i <= n;i ++ )
scanf( "%lld", &b[i] );
g[0] = -1e18;
for( int i = 1;i <= n;i ++ ) {
int t = dp[i - 1];
while( top && h[s[top]] > h[i] ) {
t = max( t, f[top] );
top --;
}
s[++ top] = i, f[top] = t;
dp[i] = g[top] = max( g[top - 1], t + b[i] );
/*
(s[top-1],i]这一段被弹出的元素高度都比i高
g[top-1]:把(s[top-1],i]这一段跟[1,s[top-1]]合并 答案就在比i矮的s[top-1]身上
t+b[i]: (s[top-1],i]单独成段 最矮的就是i
*/
}
printf( "%lld\n", dp[n] );
return 0;
}
F. Useful Edges
如果一条边 ( x , y , c ) (x,y,c) (x,y,c)和三元组 ( u , v , l ) (u,v,l) (u,v,l),满足 d i s u , x + c + d i s v , y ≤ l dis_{u,x}+c+dis_{v,y}\le l disu,x+c+disv,y≤l,则该边合法
目前来看是非常朴素的 O ( n 4 ) O(n^4) O(n4),数据范围只能承受 O ( n 3 ) O(n^3) O(n3),考虑降维(对角线想法)
d i s u , x + c + d i s v , y ≤ l ⇔ d i s v , y + c ≤ l − d i s u , x dis_{u,x}+c+dis_{v,y}\le l \Leftrightarrow dis_{v,y}+c\le l-dis_{u,x} disu,x+c+disv,y≤l⇔disv,y+c≤l−disu,x
只需要枚举 u , x , v u,x,v u,x,v便能求出 l − d i s u , x l-dis_{u,x} l−disu,x的最大值,再 u , x , y u,x,y u,x,y找到左边满足条件的边( v , y v,y v,y同地位循环)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define int long long
#define maxn 605
pair < int, int > edge[maxn * maxn / 2];
int dis[maxn][maxn], w[maxn][maxn], l[maxn][maxn];
bool flag[maxn][maxn];
int u[maxn], v[maxn];
int n, m, Q, ans;
signed main() {
memset( dis, 0x3f, sizeof( dis ) );
scanf( "%lld %lld", &n, &m );
for( int i = 1, u, v, c;i <= m;i ++ ) {
scanf( "%lld %lld %lld", &u, &v, &c );
edge[i] = make_pair( u, v );
w[u][v] = w[v][u] = dis[u][v]= dis[v][u] = c;
}
for( int i = 1;i <= n;i ++ )
dis[i][i] = 0;
for( int k = 1;k <= n;k ++ )
for( int i = 1;i <= n;i ++ )
for( int j = 1;j <= n;j ++ )
dis[i][j] = min( dis[i][j], dis[i][k] + dis[k][j] );
scanf( "%lld", &Q );
while( Q -- ) {
int u, v, x;
scanf( "%lld %lld %lld", &u, &v, &x );
l[u][v] = l[v][u] = max( l[u][v], x );
}
for( int v = 1;v <= n;v ++ ) {
for( int x = 1;x <= n;x ++ ) {
int maxx = 0;
for( int u = 1;u <= n;u ++ )
maxx = max( maxx, l[u][v] - dis[u][x] );
for( int y = 1;y <= n;y ++ ) {
if( dis[y][v] + w[x][y] <= maxx )
flag[x][y] = flag[y][x] = 1;
}
}
}
int ans = 0;
for( int i = 1;i <= m;i ++ )
if( flag[edge[i].first][edge[i].second] ) ans ++;
printf( "%lld\n", ans );
return 0;
}