文章目录
- A. Prison Break
- B. Restore Modulo
- C. Basic Diplomacy
- D. Playlist
- E. Skyline Photo
- F. Useful Edges
#709 (Div. 2)
A. Prison Break
就是每个监狱破一扇门,输出a×ba\times ba×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];elseif( ~ 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=1k=1k=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
想复杂了——就是分类大讨论
可以找两个质数,例如1e9+91e9+91e9+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
普通的DPDPDP式子:dpi=dpj+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)事实上是单调栈更新到iii时,栈里面第一个下标大于jjj的房子的美丽度
对于栈里面的第iii个元素,管辖着区间(si−1,si](s_{i-1},s_i](si−1,si],维护的是递增的单调栈
fi=maxk=1top(maxj=sk−1+1skfj+bsk)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),满足disu,x+c+disv,y≤ldis_{u,x}+c+dis_{v,y}\le ldisu,x+c+disv,y≤l,则该边合法
目前来看是非常朴素的O(n4)O(n^4)O(n4),数据范围只能承受O(n3)O(n^3)O(n3),考虑降维(对角线想法)
disu,x+c+disv,y≤l⇔disv,y+c≤l−disu,xdis_{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,vu,x,vu,x,v便能求出l−disu,xl-dis_{u,x}l−disu,x的最大值,再u,x,yu,x,yu,x,y找到左边满足条件的边(v,yv,yv,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;
}