文章目录

  • CodeForces
    • LATOKEN-Round-1(Div.1+Div.2)
      • A. Colour the Flag
      • B. Histogram Ugliness
      • C. Little Alawn's Puzzle
      • D. Lost Tree
      • E. Lost Array
      • F1. Falling Sand (Easy Version)
    • #726-Div.2
      • A. Arithmetic Array
      • B. Bad Boy
      • C. Challenging Cliffs
      • D. Deleting Divisors
      • E. Erase and Extend
    • #698-Div.2
      • A. Nezzar and Colorful Balls
      • B. Nezzar and Lucky Number
      • D. Nezzar and Board
    • GlobalRound-12
      • A. Avoid Trygub
      • B. Balls of Steel
      • C. Errich-Tac-Toe
      • D. Rating Compression
      • E. Capitalism
      • F. The Struggling Contestant
      • G. Communism
    • Educational-Round-111
      • C. Manhattan Subarrays
      • D. Excellent Arrays
      • E. Stringforces
      • F. Jumping Around
    • Educational-Round-106
      • C. Minimum Grid Path
      • D. The Number of Pairs
      • E. Chaotic Merge
      • F. Diameter Cuts
      • G. Graph Coloring
  • AtCoder
    • ARC-122
      • A - Many Formulae
      • B - Insurance
      • C - Calculator
      • D - XOR Game
      • E - Increasing LCMs
    • ABC-206
      • A - Maxi-Buying
      • B - Savings
      • C - Swappable
      • D - KAIBUNsyo
      • E - Divide Both
      • F - Interval Game 2
    • ABC-209
      • C - Not Equal
      • D - Collision
      • E - Shiritori
      • F - Deforestation
  • luogu
    • LGR-87(Div.2)
      • 远古档案馆
      • 珍珠帝王蟹
      • 天体探测仪

红色文件

  • ARC 122 F

  • LATOKEN-Round-1(Div.1+Div.2) F2,G,H

  • #726-Div.2 F

  • #727 E,F

  • #698 C,E,F

  • GR-12 G,H1,H2

明显地,CF/AT简单题也变得很难敲了o(╥﹏╥)o哭唧唧

CodeForces

LATOKEN-Round-1(Div.1+Div.2)

A. Colour the Flag

直接bfsbfsbfs一层层确定颜色,判断是否冲突即可,注意整张图都是.的情况

#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 55
queue < pair < int, int > > q;
int T, n, m;
char g[maxn][maxn], ans[maxn][maxn];bool inside( int x, int y ) {if( x < 1 || y < 1 || x > n || y > m ) return 0;else return 1;
}bool check( int x, int y, char c ) {if( ! inside( x, y ) ) return 0;if( ans[x][y] == '.' ) {if( c == 'W' ) ans[x][y] = 'R';else ans[x][y] = 'W';q.push( make_pair( x, y ) );return 0;}if( ans[x][y] == c ) return 1;else return 0;
}int main() {scanf( "%d", &T );again :while( T -- ) {while( ! q.empty() ) q.pop();scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ )scanf( "%s", g[i] + 1 );for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( g[i][j] != '.' ) {q.push( make_pair( i, j ) );ans[i][j] = g[i][j];}else ans[i][j] = '.';if( q.empty() ) {ans[1][1] = 'R';q.push( make_pair( 1, 1 ) );}while( ! q.empty() ) {int x = q.front().first, y = q.front().second; q.pop();if( check( x - 1, y, ans[x][y] ) || check( x + 1, y, ans[x][y] ) || check( x, y - 1, ans[x][y] ) || check( x, y + 1, ans[x][y] ) ) {printf( "NO\n" );goto again;}}printf( "YES\n" );for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= m;j ++ )printf( "%c", ans[i][j] );printf( "\n" );}}return 0;
}

B. Histogram Ugliness

不难发现,对于相邻的i,i+1i,i+1i,i+1,如果高度不同,那么操作消更高一个一定会是答案变小

对于相邻的i−1,i,i+1i-1,i,i+1i−1,i,i+1而言,假设最高的是iii,那么消到i−1,i+1i-1,i+1i−1,i+1两者较高高度时答案最小

再往下消答案不变,所以直接扫一遍找中间iii是最高进行消即可

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 400005
int T, n;
int a[maxn];int Fabs( int x ) {return x < 0 ? -x : x;
}signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld", &n );a[n + 1] = 0;for( int i = 1;i <= n;i ++ )scanf( "%lld", &a[i] );int ans = 0;for( int i = 1;i <= n;i ++ ) {int l = a[i - 1], r = a[i + 1];if( a[i] > l && a[i] > r ) {int maxh = max( l, r );ans += a[i] - maxh;a[i] = maxh;}}for( int i = 1;i <= n + 1;i ++ )ans += Fabs( a[i] - a[i - 1] );printf( "%lld\n", ans );}return 0;
}

C. Little Alawn’s Puzzle

因为两个都是排列,所以当交换(i,j)(i,j)(i,j)后就必须不断的交换重复数字来抵消影响

其实是找环的个数(sis_isi​通过与ttt的映射最后回到sis_isi​的路径条数)

答案为2cnt2^{cnt}2cnt

#include <cstdio>
#define mod 1000000007
#define int long long
#define maxn 400005
int T, n;
int s[maxn], t[maxn], g[maxn];
bool vis[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;
}signed main() {scanf( "%lld", &T );again :while( T -- ) {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )vis[i] = 0, scanf( "%lld", &s[i] );for( int i = 1;i <= n;i ++ )scanf( "%lld", &t[i] );for( int i = 1;i <= n;i ++ )if( s[i] == t[i] ) {printf( "0\n" );goto again;}else g[s[i]] = t[i];int cnt = 0;for( int i = 1;i <= n;i ++ ) {if( vis[s[i]] ) continue;cnt ++;int now = s[i], flag = 1;while( flag || now != s[i] ) {vis[now] = 1;now = g[now];flag = 0;}}printf( "%lld\n", qkpow( 2, cnt ) );}return 0;
}

D. Lost Tree

显然,不管询问哪一个点,一定有点与之距离为111(有直接树边相连)

先问一次1,把整棵树按距离分层建起来,再按奇偶分

哪边含的点少问哪边(问一层里的所有点,就把相邻两层的信息点找完了)

这样上线就能卡满⌈n2⌉\lceil\frac{n}{2}\rceil⌈2n​⌉

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 2005
vector < int > g[2];
vector < pair < int, int > > ans;
int n;
int d[maxn];
bool vis[maxn][maxn];void ask( int x ) {printf( "? %d\n", x );fflush( stdout );for( int i = 1;i <= n;i ++ )scanf( "%d", &d[i] );
}int main() {scanf( "%d", &n );ask( 1 );for( int i = 2;i <= n;i ++ )g[d[i] & 1].push_back( i );if( g[0].size() > g[1].size() ) swap( g[0], g[1] );for( int i = 1;i <= n;i ++ )if( d[i] == 1 ) ans.push_back( make_pair( 1, i ) ), vis[1][i] = vis[i][1] = 1;for( int i = 0;i < g[0].size();i ++ ) {ask( g[0][i] );for( int j = 1;j <= n;j ++ )if( d[j] == 1 ) {if( vis[g[0][i]][j] ) continue;else {vis[g[0][i]][j] = vis[j][g[0][i]] = 1;ans.push_back( make_pair( g[0][i], j ) );}}}printf( "!\n" );for( int i = 0;i < ans.size();i ++ )printf( "%d %d\n", ans[i].first, ans[i].second );return 0;
}

E. Lost Array

求整个序列的异或和 ⇔\Leftrightarrow⇔ 每个数的操作次数为奇数1-0-1-0-1...

两种方法,bfs最短路/dp(本质是一样的)

设fi:f_i:fi​: 到目前操作为止一共有iii个数的值没有被抵消

枚举下一次有jjj个数重复(这jjj个数将变成没有贡献,而多了k−jk-jk−j个数的异或和)

最后fnf_nfn​即为最小操作次数

记录下每次最优转移的前驱以及重复个数,搜索还原即可

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 505
#define inf 0x3f3f3f3f
queue < int > q;
int n, k, ans, ret;
int f[maxn], pre[maxn], val[maxn];
bool vis[maxn];void dfs( int now ) {if( ~ pre[now] ) dfs( pre[now] );else return;//cnt1:appeared//cnt2:disappearedprintf( "?" );for( int cnt1 = val[now], cnt2 = k - val[now], i = 1;cnt1 || cnt2;i ++ ) {if( vis[i] ) {if( cnt1 ) vis[i] ^= 1, printf( " %d", i ), cnt1 --;else;} else {if( cnt2 ) vis[i] ^= 1, printf( " %d", i ), cnt2 --;else;}}printf( "\n" );fflush( stdout );scanf( "%d", &ans );ret ^= ans;
}int main() {memset( pre, -1, sizeof( pre ) );scanf( "%d %d", &n, &k );memset( f, 0x3f, sizeof( f ) );f[0] = 0;q.push( 0 );while( ! q.empty() ) {int used = q.front(); q.pop();//used: how many numbers have contributedfor( int i = 0;i <= k;i ++ ) {//next query has tot in i numbers same to usedif( i <= used && k - i <= n - used ) {//k<=n-used+iint nxt = used - i + k - i;if( nxt > 0 && nxt <= n && f[used] + 1 < f[nxt] ) {f[nxt] = f[used] + 1;val[nxt] = i;pre[nxt] = used;q.push( nxt );}}}}if( f[n] == inf ) return ! printf( "-1\n" );else dfs( n );printf( "! %d\n", ret );return 0;
}

F1. Falling Sand (Easy Version)

把所有土块移出网格

把自动土块能干扰的土块连有向边,这样的暴力建图是n2n^2n2的

事实上只需要知道上下左右最先干扰的土块,然后影响传递即可,建图锐减至4n4n4n

图中可能包含强联通图(其中一个松动,最后都会出局),缩点tarjan

那么最后其实就是找入度为000的土块松动(没有土块能触发它们)

PS:简单版保证aia_iai​等于该列土块数量,所以俺干脆直接不读了

#include <map>
#include <stack>
#include <cstdio>
#include <vector>
using namespace std;
#define maxn 400005
stack < int > sta;
vector < int > G[maxn], mp[maxn], id[maxn];
int n, m, cnt, Cnt, tot;
char s[maxn];
int dfn[maxn], low[maxn], last[maxn], scc[maxn], d[maxn];void addEdge( int u, int v ) {G[u].push_back( v );
}void tarjan( int u ) {dfn[u] = low[u] = ++ Cnt;sta.push( u );for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( ! dfn[v] ) {tarjan( v );low[u] = min( low[u], low[v] );}else if( ! scc[v] )low[u] = min( low[u], dfn[v] );}if( low[u] == dfn[u] ) {int v; ++ tot;do {v = sta.top(); sta.pop();scc[v] = tot;} while( v != u );}
}int main() {scanf( "%d %d", &n, &m );mp[0].resize( m + 2 ), id[0].resize( m + 2 );for( int i = 1;i <= n;i ++ ) {mp[i].resize( m + 2 ), id[i].resize( m + 2 );scanf( "%s", s + 1 );for( int j = 1;j <= m;j ++ )mp[i][j] = s[j], id[i][j] = ++ cnt;}for( int i = n;i;i -- ) {for( int j = 1;j <= m;j ++ ) {if( mp[i][j] == '.' ) continue;if( mp[i - 1][j] == '#' ) addEdge( id[i][j], id[i - 1][j] );if( last[j] ) addEdge( id[i][j], last[j] );if( mp[i][j - 1] == '#' ) addEdge( id[i][j], id[i][j - 1] );else if( last[j - 1] ) addEdge( id[i][j], last[j - 1] );if( mp[i][j + 1] == '#' ) addEdge( id[i][j], id[i][j + 1] );else if( last[j + 1] ) addEdge( id[i][j], last[j + 1] );}for( int j = 1;j <= m;j ++ )if( mp[i][j] == '#' ) last[j] = id[i][j];}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= m;j ++ )if( mp[i][j] == '#' && ! dfn[id[i][j]] ) tarjan( id[i][j] );for( int i = 1;i <= cnt;i ++ )for( int j = 0;j < G[i].size();j ++ )if( scc[i] ^ scc[G[i][j]] ) ++ d[scc[G[i][j]]];int ans = 0;for( int i = 1;i <= tot;i ++ )ans += ( d[i] == 0 );printf( "%d\n", ans );return 0;
}

#726-Div.2

A. Arithmetic Array

总和如果比nnn大,意味着需要不断补000位置,sum-n

反之,只需要加一个正整数,1

B. Bad Boy

最远的两端(1,1)(n,m)

C. Challenging Cliffs

排序后直接找差距最小的两个(有多对,任选一对即可)然后按高度,比第一座高的按高度递增排列,最高的后面肯定接的是最矮的(出去两个标志建筑)没选的矮的递增排列,形成一个斜Z

D. Deleting Divisors

结论先猜后证yyds

  • case1: n=2k+1n=2k+1n=2k+1 奇数 Bob
  • n=2kn=2kn=2k 偶数
    • case2: n=2tn=2^tn=2t 222的幂 t=2T+1->Bob t=2T->Alice
    • case3: n=2t×(2p+1)n=2^t\times (2p+1)n=2t×(2p+1) 非222的幂 Alice

case1

减去一个奇数除数(因为所有的除数都是奇数),得到一个不是222的幂的偶数

如果ddd是nnn的除数,那么n−dn−dn−d也必须能被ddd整除,因为ddd是奇数,n−dn−dn−d不是222的幂

case3

减去这个奇数除数,我们将得到n−dn−dn−d是奇数

要么对方拿到的奇数是质数——直接失败;要么就是case1操作后还一个不是222的幂的偶数

所以总是不败的,因此这种局面先手必胜;故此可以退出case1奇数局面先手必败

case2

要么减半nnn要么使nnn成为不是222的幂的偶数(已经证明了这是另一个玩家必胜的状态)

唯一的方法就只能是将nnn减半,使其成为222的另一个幂,知道有一个拿到222,质数输掉比赛

所以局面跟log⁡2n\log_2nlog2​n的奇偶挂钩

E. Erase and Extend

对于简单版本,直接暴力填充完所有位置,然后分情况

如果s1<sis_1<s_is1​<si​那么从这里开始以s1,i−1s_{1,i-1}s1,i−1​为周期直接重新填充

如果s1=sis_1=s_is1​=si​就从iii开始以长度为i−1i-1i−1与s1,i−1s_{1,i-1}s1,i−1​一一对比,如果更小,也要选择重新填充

#include <cstdio>
#define maxn 10005
int n, k;
char s[maxn];int main() {scanf( "%d %d %s", &n, &k, s + 1 );while( n < k ) {for( int i = 1;i <= n;i ++ )s[i + n] = s[i];n <<= 1;}int now = 1;for( int i = 2;i <= k;i ++ ) {if( s[now] > s[i] ) continue;else if( s[now] < s[i] ) {int t = 1;for( int j = i;j <= k;j ++ ) {s[j] = s[t];t ++;if( t == i ) t = 1;}}else {bool flag = 0;for( int j = i;j <= ( i - 1 << 1 );j ++ )if( s[j - i + 1] < s[j] ) {flag = 1;break;}else if( s[j - i + 1] > s[j] ) break;if( flag ) {int t = 1;for( int j = i;j <= k;j ++ ) {s[j] = s[t];t ++;if( t == i ) t = 1;}   }   }}for( int i = 1;i <= k;i ++ )printf( "%c", s[i] );return 0;
}

困难版本 比简单版本还好敲 不能暴力的O(nk)O(nk)O(nk)走人

用另外的数组存模板串,最后的答案为模板串的周期循环

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 500005
int n, k;
char s[maxn];int main() {scanf( "%d %d %s", &n, &k, s + 1 );int len = 1;for( int i = 2;i <= min( n, k );i ++ ) {int pos = ( i - 1 ) % len + 1;if( s[pos] > s[i] ) len = i;else if( s[pos] < s[i] ) break;}for( int i = 1;i <= k;i ++ )printf( "%c", s[( i - 1 ) % len + 1] );return 0;
}

#698-Div.2

A. Nezzar and Colorful Balls

求出现次数最多的数,输入还保证递增,不说了

B. Nezzar and Lucky Number

将xxx分裂成几个ddd,发现在ddd前面加的数都是101010的倍数

我就想能不能把xxx的个位卡掉后,剩下的101010的倍数就随便塞到分裂的ddd前面

这就转化为了同余问题

#include <cstdio>
int T, Q, d, x;int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d %d", &Q, &d );while( Q -- ) {scanf( "%d", &x );int i;for( i = 1;i < 10;i ++ )if( ( d * i ) % 10 == x % 10 ) break;if( i * d > x ) printf( "NO\n" );else printf( "YES\n" );}}return 0;
}

D. Nezzar and Board

注意到2x−y2x-y2x−y这个操作翻译成数学意思,其实是在数轴上yyy关于xxx的对称点

操作可以无限下去,到最后肯定是两点之间的距离相等为止,否则一定会再次操作定位新的坐标点

而这个距离就是原数组二者之间的gcdgcdgcd

这道题很需要数学的直觉把我是SB再见

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define int long long
#define maxn 200005
int T, n, k;
int MS[maxn];int gcd( int x, int y ) {if( x < y ) swap( x, y );if( ! y ) return x;else return gcd( y, x % y );
}signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld", &n, &k );for( int i = 1;i <= n;i ++ )scanf( "%lld", &MS[i] );sort( MS + 1, MS + n + 1 );int g = MS[2] - MS[1];for( int i = 3;i <= n;i ++ )g = gcd( g, MS[i] - MS[i - 1] );if( ( k - MS[1] ) % g ) printf( "NO\n" );else printf( "YES\n" );} return 0;
}

GlobalRound-12

A. Avoid Trygub

直接字母排序输出

B. Balls of Steel

发现结果只有可能是1/-1 ,如果第一次两个点无法合并那么后面就不可能通过中转点合并,因为点的位置会发生改变,样例三就是提醒这一点

C. Errich-Tac-Toe

将行列按(i+j)%3(i+j)\%3(i+j)%3分类(染色),选择颜色中最小的特殊格子(属于某三个连续情况)个数
困难情况无非是还要按X/OX/OX/O分成六类
easy version

#include <cstdio>
#define maxn 305
int T, n;
char s[maxn][maxn];
int cnt[5];bool check( int i, int j ) {if( s[i][j] != 'X' ) return 0;if( j > 1 && s[i][j - 2] == 'X' && s[i][j - 1] == 'X' ) return 1;if( j < n && s[i][j - 1] == 'X' && s[i][j + 1] == 'X' ) return 1;if( j < n - 1 && s[i][j + 1] == 'X' && s[i][j + 2] == 'X' ) return 1;if( i > 1 && s[i - 2][j] == 'X' && s[i - 1][j] == 'X' ) return 1;if( i < n && s[i - 1][j] == 'X' && s[i + 1][j] == 'X' ) return 1;if( i < n - 1 && s[i + 1][j] == 'X' && s[i + 2][j] == 'X' ) return 1;return 0;
}int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%s", s[i] + 1 );cnt[0] = cnt[1] = cnt[2] = 0;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )if( check( i, j ) ) cnt[( i + j ) % 3] ++;int minn = 1e9, ip;for( int i = 0;i < 3;i ++ )if( cnt[i] < minn ) minn = cnt[i], ip = i;for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= n;j ++ )if( check( i, j ) && ( ( i + j ) % 3 == ip ) )printf( "O" );elseprintf( "%c", s[i][j] );printf( "\n" );}}return 0;
}

hard version

#include <cstdio>
#include <cstring>
#define maxn 305
int T, n;
char s[maxn][maxn];
int cnt[2][3];void solve( int c1, int c0 ) {for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= n;j ++ )if( ( i + j ) % 3 == c1 && s[i][j] == 'X' )printf( "O" );else if( ( i + j ) % 3 == c0 && s[i][j] == 'O' )printf( "X" );elseprintf( "%c", s[i][j] );printf( "\n" );}
}int main() {scanf( "%d", &T );again :while( T -- ) {scanf( "%d", &n );int k = 0;memset( cnt, 0, sizeof( cnt ) );for( int i = 1;i <= n;i ++ ) {scanf( "%s", s[i] + 1 );for( int j = 1;j <= n;j ++ )switch( s[i][j] ) {case 'X' : {cnt[1][( i + j ) % 3] ++;k ++;break;}case 'O' : {cnt[0][( i + j ) % 3] ++;k ++;break;}}}for( int i = 0;i < 3;i ++ )for( int j = 0;j < 3;j ++ )if( i == j ) continue;else if( cnt[1][i] + cnt[0][j] <= k / 3 ) {solve( i, j );goto again;}}return 0;
}

D. Rating Compression

  • 如果k=1k=1k=1,要求原序列是个排列
  • 如果k=nk=nk=n,要求序列有111
  • 如果1<k<n1<k<n1<k<n,如果最后的结果为排列,则111只能出现一次,发现如果111不出现在1/n1/n1/n位置上,就必定会出现≥2≥2≥2次
    综上,得出规律,定义l,rl,rl,r,要求对于iii一定出现在l/rl/rl/r,递归下去
#include <cstdio>
#define maxn 300005
int T, n;
int a[maxn], cnt[maxn];
bool ans[maxn];int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )cnt[i] = ans[i] = 0;for( int i = 1;i <= n;i ++ )scanf( "%d", &a[i] ), cnt[a[i]] ++;for( int i = 1;i <= n;i ++ )if( cnt[i] == 1 ) continue;else goto pass;ans[1] = 1;pass :ans[n] = cnt[1];int l = 1, r = n;for( int i = n - 1;i;i -- ) {int nxt = n - i;cnt[nxt] --;if( ! cnt[nxt] && ( a[l] == nxt || a[r] == nxt ) && cnt[nxt + 1] ) {if( a[l] == nxt ) l ++;else r --;ans[i] = 1;}else break;}for( int i = 1;i <= n;i ++ )printf( "%d", ans[i] );printf( "\n" );}return 0;
}

E. Capitalism

∣au−av∣=1→|a_u-a_v|=1\rightarrow∣au​−av​∣=1→ 差分约束 →\rightarrow→ 图论最短路
确定方向的,相当于1≤av−au≤11\le a_v-a_u\le 11≤av​−au​≤1,(u→v)=1,(v→u)=1(u\rightarrow v)=1,(v\rightarrow u)=1(u→v)=1,(v→u)=1
未知方向的,相当于−1≤av−au≤1-1\le a_v-a_u\le 1−1≤av​−au​≤1,(u→v)=(v→u)=1(u\rightarrow v)=(v\rightarrow u)=1(u→v)=(v→u)=1
不能出现负环,嫉妒冲突了
而且一定是个二部图au≠ava_u≠a_vau​​=av​,可以用二分图染色判断

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 205
int n, m;
int dis[maxn][maxn];
bool vis[maxn];
int c[maxn];int Fabs( int x ) {return x < 0 ? -x : x;
}void dfs( int now ) {vis[now] = 1;for( int i = 1;i <= n;i ++ )if( i != now && dis[now][i] <= n ) {if( ! vis[i] ) c[i] = c[now] ^ 1, dfs( i );else if( c[i] == c[now] ) {printf( "NO\n" );exit( 0 );}}
}int main() {scanf( "%d %d", &n, &m );memset( dis, 0x3f, sizeof( dis ) );for( int i = 1;i <= n;i ++ ) dis[i][i] = 0;for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%d %d %d", &u, &v, &w );dis[u][v] = 1, dis[v][u] = w ? -1 : 1;}dfs( 1 );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] );for( int i = 1;i <= n;i ++ )if( dis[i][i] < 0 ) return ! printf( "NO\n" );   int ans = -1, pos;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )if( Fabs( dis[i][j] ) > ans ) ans = Fabs( dis[i][j] ), pos = i;printf( "YES\n%d\n", ans );for( int i = 1;i <= n;i ++ )   printf( "%d ", dis[pos][i] + n );return 0;
}

F. The Struggling Contestant

不相邻的最小,显然要将相邻的数绑在一起移动

设kkk表示连续相同的对数,f(i)f(i)f(i)表示iii成为端点的次数,可行解的条件为max⁡(f(i))≤k+2\max\bigg(f(i)\bigg)\le k+2max(f(i))≤k+2

假设有k+1k+1k+1段,那么有2k+22k+22k+2个端点,除掉最左边和最右边的两个端点

还有2k2k2k个端点,每两个端点只能选一个,有kkk个,加在一起最多也就是k+2k+2k+2

因此最大值为k+2k+2k+2

接下来证明可以构造出解来

设xxx为出现次数最多的数,y(≠x)y(≠x)y(​=x)为两个段的端点,进行合并,则k,f(x),f(y)k,f(x),f(y)k,f(x),f(y)均−1-1−1,仍满足f(x),f(y)≤k+2f(x),f(y)\le k+2f(x),f(y)≤k+2的关系式

对于z(≠x≠y)z(≠x≠y)z(​=x​=y),合并前f(z)≤2k+2−f(x)≤2k+2−f(z)⇒f(z)≤k+1f(z)\le 2k+2-f(x)\le 2k+2-f(z)\Rightarrow f(z)\le k+1f(z)≤2k+2−f(x)≤2k+2−f(z)⇒f(z)≤k+1合并后k−1k-1k−1,仍满足f(z)≤k+2f(z)\le k+2f(z)≤k+2

这样每次将kkk减一至零,一共kkk次操作,整个过程都是f()≤k+2f()\le k+2f()≤k+2

现在考虑max⁡(f(x))>k+2\max\bigg(f(x)\bigg)>k+2max(f(x))>k+2的情况,想办法切割成≤\le≤

如果在切割时包含了xxx,那么f(x),kf(x),kf(x),k均增加,情况并未发生改变

所以需要在y(≠x),z(≠x)y(≠x),z(≠x)y(​=x),z(​=x)两个点之间进行切割,这样会导致kkk增加

重复这样的操作直到变化,操作次数为max⁡(f(x))−(k+2)\max\bigg(f(x)\bigg)-(k+2)max(f(x))−(k+2)

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 100005
int T, n;
int a[maxn], cnt[maxn], f[maxn];int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d",&n );for( int i = 1;i <= n;i ++ )f[i] = cnt[i] = 0;int k = 0;for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );cnt[a[i]] ++;if( a[i] == a[i - 1] ) {k ++;f[a[i]] += 2;}}f[a[1]] ++, f[a[n]] ++;int max_cnt = 0, max_f = 0;for( int i = 1;i <= n;i ++ ) {max_cnt = max( max_cnt, cnt[i] );max_f = max( max_f, f[i] );}if( ( max_cnt << 1 ) > n + 1 ) printf( "-1\n" );else printf( "%d\n", k + max( 0, max_f - ( k + 2 ) ) );}return 0;
}

G. Communism

去掉6个字母恰好只剩20个,刚好是可以状压的范围 用意令人深思

令li:l_i:li​:字符集为iii的最早出现位置,ri:r_i:ri​:字符集为iii的最晚出现位置,cnti:cnt_i:cnti​:字符集为iii出现次数

定义dps:dp_s:dps​:字符集为sss时,能否被转移到

  • dp0=1dp_0=1dp0​=1

  • 当s={i}s=\{i\}s={i}只有一个字符时,如果满足k×(ri−li+1)≤cnti⇒dps=1k\times(r_i-l_i+1)\le cnt_i\Rightarrow dp_s=1k×(ri​−li​+1)≤cnti​⇒dps​=1

  • 对于某一个字符iii,一开始无法操作,如果可以先操作s−is-is−i剩下字符再操作iii就可以

    [i∈s]⋂[dps−x=1]⋂[k×(rs−ls+1)≤cnts]⇒dps=1[i∈s]\bigcap[dp_{s-x}=1]\bigcap[k\times(r_s-l_s+1)\le cnt_s]\Rightarrow dp_s=1[i∈s]⋂[dps−x​=1]⋂[k×(rs​−ls​+1)≤cnts​]⇒dps​=1

  • 当字符集为两不相交的字符集的并,且两字符集都可以操作,那么合并后的字符集也同样可以操作

    [s=i⋃j]⋂[i⋂j=∅]⋂[dpi=dpj=1]⇒dps=1[s=i\bigcup j]\bigcap[i\bigcap j=\empty]\bigcap[dp_i=dp_j=1]\Rightarrow dp_s=1[s=i⋃j]⋂[i⋂j=∅]⋂[dpi​=dpj​=1]⇒dps​=1

但是因为性质四,涉及子集枚举,O(320)O(3^{20})O(320),考虑优化

性质:当且仅当两个字符集的管辖区间li,ril_i,r_ili​,ri​不相交,才需要考虑这样的转移

因为如果区间有相交,那么一定是性质三的转移不差于性质四转移

两段相交区间,唯一变的就是r−l+1r-l+1r−l+1,变小,则可以适应更大的kkk,更容易转移,所以选择一个吃掉另一个再转移

那么这个优化就可以按照lll排序,前缀和优化

最后如果dpU⨁i=1dp_{U\bigoplus i}=1dpU⨁i​=1,则该字符是可以被操作成整字符串的

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define maxn 5005
#define alpha 150
#define maxs 1 << 20
int n, a, b;
bool dp[maxs];
char S[maxn], MS[alpha];
int id[alpha], l[maxs], r[maxs], cnt[maxs], s[maxn];bool cmp( int x, int y ) {return l[1 << x] < l[1 << y];
}int lowbit( int x ) {return x & ( -x );
}int main() {int n, a, b;scanf( "%d %d %d %s", &n, &a, &b, S + 1 );int ip = -1, U = 0;for( char i = 'a';i <= 'z';i ++ )if( i == 't' || i == 'r' || i == 'y' || i == 'g' || i == 'u' || i == 'b' )continue;elseid[i] = ++ ip, MS[ip] = i;for( int i = 1;i <= n;i ++ )s[i] = id[S[i]], U |= ( 1 << s[i] );for( int i = 0;i < 20;i ++ )id[i] = i, l[1 << i] = n;for( int i = 1;i <= n;i ++ ) {l[1 << s[i]] = min( i, l[1 << s[i]] );r[1 << s[i]] = i;cnt[1 << s[i]] ++;}sort( id, id + 20, cmp );for( int i = 0;i < maxs;i ++ )if( i ^ lowbit( i ) ) {l[i] = min( l[i ^ lowbit( i )], l[lowbit( i )] );r[i] = max( r[i ^ lowbit( i )], r[lowbit( i )] );cnt[i] = cnt[i ^ lowbit( i )] + cnt[lowbit( i )];}dp[0] = 1;for( int i = 1;i < maxs;i ++ ) {if( ( i & U ) != i ) continue;if( ( r[i] - l[i] + 1 ) * a <= cnt[i] * b ) {if( i == lowbit( i ) ) dp[i] = 1;else {for( int j = 0;j < 20;j ++ )if( 1 << j & i ) dp[i] |= dp[1 << j ^ i];}}if( ! dp[i] ) {int k = 0;for( int j = 0;j < 20;j ++ ) {k = ( 1 << id[j] | k ) & i;dp[i] |= dp[i ^ k] & dp[k];}}}int ans = 0;for( int i = 0;i < 20;i ++ )if( dp[1 << i ^ U] ) ans ++;printf( "%d", ans );for( int i = 0;i < 20;i ++ )if( dp[1 << i ^ U] )printf( " %c", MS[i] );return 0;
}

Educational-Round-111

C. Manhattan Subarrays

曼哈顿距离,发现若[l,r][l,r][l,r]内存在三个 递增 / 递减 / 两平加一 的点,则一定不是好区间

因此发现,好区间的长度最多不超过4,直接暴力可做

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 200005
int T, n, ans;
int a[maxn];int Fabs( int x ) {return x < 0 ? -x : x;
}int dis( int i, int j ) {return Fabs( i - j ) + Fabs( a[i] - a[j] );
}void calc( int l, int r ) {for( int i = l;i <= r;i ++ )for( int j = i + 1;j <= r;j ++ )for( int k = j + 1;k <= r;k ++ )if( dis( i, j ) + dis( j, k ) == dis( i, k ) ) return;ans ++;
}int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d", &a[i] );ans = 0;for( int i = 1;i <= n;i ++ )for( int j = i;j <= min( n, i + 3 );j ++ )calc( i, j );printf( "%d\n", ans );}return 0;
}

D. Excellent Arrays

  • ai+aj=i+j⇔ai−i=−(aj−j)a_i+a_j=i+j\Leftrightarrow a_i-i=-(a_j-j)ai​+aj​=i+j⇔ai​−i=−(aj​−j)

    定义wi=ai−i⇒ai−i=wi→wi=−wjw_i=a_i-i\Rightarrow a_i-i=w_i\rightarrow w_i=-w_jwi​=ai​−i⇒ai​−i=wi​→wi​=−wj​

  • {ai+aj=i+jaj+ak=j+kai+ak=i+k⇒{wi=−wjwj=−wkwi=−wk\begin{cases} a_i+a_j=i+j\\ a_j+a_k=j+k\\ a_i+a_k=i+k\\ \end{cases} \Rightarrow \begin{cases} w_i=-w_j w_j=-w_k w_i=-w_k \end{cases} ⎩⎪⎨⎪⎧​ai​+aj​=i+jaj​+ak​=j+kai​+ak​=i+k​⇒{wi​=−wj​wj​=−wk​wi​=−wk​​

    若想要三个中任意两个都能配对,当且仅当ai=ia_i=iai​=i,但不满足好序列要求

    因此所有数只能分成两部分,不同部分间彼此配对

  • 配对的数量最大化,显然是一半的数为www,另外一半的数为−w-w−w

    这样配对数量最多,可以达到(n2)2(\frac{n}{2})^2(2n​)2级(正方形面积问题)

    F(a)=⌊n2⌋⋅⌈n2⌉F(a)=\lfloor\frac{n}{2}\rfloor·\lceil{\frac{n}{2}\rceil}F(a)=⌊2n​⌋⋅⌈2n​⌉

  • 但是有的iii,可能只能是www类型,有的可能只能是−w-w−w类型, 有的二者皆可

  • 定义half=⌊n2⌋half=\lfloor\frac{n}{2}\rfloorhalf=⌊2n​⌋,不同范围www的计数(假设w>0w>0w>0)

    有两个不等式
    {l≤ai=wi+i≤rl≤ai=−wi+i≤r⇒{l−i≤wi≤r−ii−r≤wi≤i−l\begin{cases} l\le a_i=w_i+i\le r\\ l\le a_i=-w_i+i\le r \end{cases} \Rightarrow \begin{cases} l-i\le w_i\le r-i\\ i-r\le w_i\le i-l \end{cases} {l≤ai​=wi​+i≤rl≤ai​=−wi​+i≤r​⇒{l−i≤wi​≤r−ii−r≤wi​≤i−l​

    • 如果max⁡(l−i,i−r)≤wi≤min⁡(r−i,i−l)⇒w≤min⁡(r−n,1−l)\max(l-i,i-r)\le w_i\le \min(r-i,i-l)\Rightarrow w\le \min(r-n,1-l)max(l−i,i−r)≤wi​≤min(r−i,i−l)⇒w≤min(r−n,1−l),则整个[l,r][l,r][l,r]都是二者类型

      • n=2k→Cnhalfn=2k\rightarrow C_{n}^{half}n=2k→Cnhalf​
      • n=2k+1→Cnhalf+Cnhalf+1n=2k+1\rightarrow C_{n}^{half}+C_{n}^{half+1}n=2k+1→Cnhalf​+Cnhalf+1​
    • 如果w>min⁡(r−n,1−l)w>\min(r-n,1-l)w>min(r−n,1−l),则∀i∈[1,L)ai\forall_{i∈[1,L)}a_i∀i∈[1,L)​ai​只能是www类型,∀i∈(R,n]ai\forall_{i∈(R,n]}a_i∀i∈(R,n]​ai​只能是−w-w−w类型

      {L=min⁡(1,l+w);R=max⁡(n,r−w)}\bigg\{L=\min(1,l+w);R=\max(n,r-w)\bigg\}{L=min(1,l+w);R=max(n,r−w)}

      R−L+1R-L+1R−L+1个则是自由二者皆可

      枚举www,当L,RL,RL,R冲突时break

      • n=2k→CR−L+1half−(L−1)n=2k\rightarrow C_{R-L+1}^{half-(L-1)}n=2k→CR−L+1half−(L−1)​
      • n=2k+1→CR−L+1half−(L−1)+CR−L+1half−(L−1)+1n=2k+1\rightarrow C_{R-L+1}^{half-(L-1)}+C_{R-L+1}^{half-(L-1)+1}n=2k+1→CR−L+1half−(L−1)​+CR−L+1half−(L−1)+1​

时间复杂度O(nlog⁡M)O(n\log M)O(nlogM)

#include <cstdio>
#include <iostream>
using namespace std;
#define mod 1000000007
#define int long long
#define maxn 200000
int fac[maxn << 1], inv[maxn << 1];
int T, n, l, r;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;
}int C( int n, int m ) {if( n < 0 || n < m || m < 0 ) return 0;else return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {fac[0] = inv[0] = 1;for( int i = 1;i <= maxn;i ++ )fac[i] = fac[i - 1] * i % mod;inv[maxn] = qkpow( fac[maxn], mod - 2 );for( int i = maxn - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;scanf( "%lld", &T );while( T -- ) {scanf( "%lld %lld %lld", &n, &l, &r );int k = min( 1 - l, r - n ), half = n >> 1, ans = 0;if( n & 1 ) ans = ( k * C( n, half ) % mod + k * C( n, half + 1 ) % mod ) % mod;elseans = k * C( n, half );while( ++ k ) {int L = max( 1ll, l + k ), R = min( r - k, n ), len = R - L + 1;if( len < 0 ) break;elseif( n & 1 )ans = ( ans + C( len, half - ( L - 1 ) ) + C( len, half - ( L - 1 ) + 1 ) ) % mod;elseans = ( ans + C( len, half - ( L - 1 ) ) ) % mod;}printf( "%lld\n", ans );}return 0;
}

E. Stringforces

  • 二分答案长度len

  • check长度,想要在nnn以内使得前kkk个罗马小写字符都连续出现至少len

    转化为让前kkk个小写字符都连续出现至少一次的至少len个的最短长度

  • 设dps:dp_s:dps​: 已经构造的字符集为s,字符集内每个字符都满足要求的最短长度

  • 设posi,j:pos_{i,j}:posi,j​: 以i往后的某个位置开始填连续lenj字符的结束位置

    需要保证,整段除了?就是j,定义lasti:last_i:lasti​: i字符上次出现位置,枚举字符判断是否在段内即可

  • 转移:dps∣1<<j=min⁡{posdps+1,j}dp_{s|1<<j}=\min\{pos_{dp_s+1,j}\}dps∣1<<j​=min{posdps​+1,j​}

  • 最后保证长度≤n\le n≤n即可

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 200005
#define alpha 17
int n, k, S;
char s[maxn];
int dp[1 << alpha], last[maxn];
int pos[maxn][alpha];
bool check( int len ) {memset( pos, 0x7f, sizeof( pos ) );memset( last, 0x7f, sizeof( last ) );for( int i = n;i;i -- ) {if( s[i] != '?' ) last[s[i] - 'a'] = i; else;for( int j = 0;j < k;j ++ ) {pos[i][j] = ( i + len - 1 <= n ) ? i + len - 1 : pos[i + 1][j];for( int w = 0;w < k;w ++ )if( j == w ) continue;else if( last[w] <= i + len - 1 ) {pos[i][j] = pos[i + 1][j];break;} else;}}memset( dp, 0x7f, sizeof( dp ) );dp[0] = 0;for( int i = 0;i < S;i ++ )for( int j = 0;j < k;j ++ )if( ( 1 << j & i ) || dp[i] == inf ) continue;else dp[1 << j | i] = min( dp[1 << j | i], pos[dp[i] + 1][j] );return dp[S - 1] <= n;
}int main() {scanf( "%d %d %s", &n, &k, s + 1 );S = 1 << k;int l = 0, r = n / k, ans = 0;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;
}

F. Jumping Around

boruvka最小生成树
既有kruskal的并查集合并,又有prim的扩展,因而正确性得到保证

  • 初始化,每个点为一个联通块
  • 根据题目选择数据结构,快速找到两个联通块之间的最大/最小边权
  • 并查集按秩合并,建图
  • 利用图的dfs树上的lca等信息求解

对于此题,两点互达的最小需要kkk即为边权
具体代码:
枚举联通块,set维护点,对于联通块内的每个点
先把同联通块的点扔出去
set查找距离点最近的前后点(隶属不同联通块)
边权∣∣ai−ax∣−d∣\big||a_i-a_x|-d\big|∣∣​∣ai​−ax​∣−d∣∣​
建图,从sss开始走dfs树,记录到每个点途经的最大值
那就是最小生成树的瓶颈边,判断给定的kkk与该边的关系即可

#include <set>
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 1000005
struct node {int u, v, w;node(){}node( int U, int V, int W ) {u = U, v = V, w = W;}
};
set < int > pos;
set < int > :: iterator it;
vector < int > MS[maxn];
vector < pair < int, int > > G[maxn];
int n, Q, s, d;
int ans[maxn], x[maxn], f[maxn], id[maxn];int Fabs( int x ) {return x < 0 ? -x : x;
}bool operator < ( node x, node y ) {return x.w < y.w;
}void dfs( int u, int fa, int val ) {ans[u] = val;for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i].first;if( v == fa ) continue;else dfs( v, u, max( val, G[u][i].second ) );}
}bool merge( int u, int v ) {u = f[u], v = f[v]; if( u == v ) return 0;else {if( MS[u].size() < MS[v].size() ) swap( u, v );for( int i = 0;i < MS[v].size();i ++ )f[MS[v][i]] = u, MS[u].push_back( MS[v][i] );MS[v].clear();return 1;}
}int main() {scanf( "%d %d %d %d", &n, &Q, &s, &d );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &x[i] );MS[i].push_back( i );f[i] = id[x[i]] = i;pos.insert( x[i] );}int cnt = n;while( cnt > 1 ) {vector < node > edge;for( int i = 1;i <= n;i ++ ) {if( ! MS[i].size() ) continue;else {for( int j : MS[i] ) pos.erase( x[j] );node minn = node( 0, 0, inf );for( int j : MS[i] )for( int k : { -d, d } ) {it = pos.lower_bound( x[j] + k );if( it != pos.end() )minn = min( minn, node( j, id[*it], Fabs( Fabs( x[j] - ( *it ) ) - d ) ) );if( it != pos.begin() )it --, minn = min( minn, node( j, id[*it], Fabs( Fabs( x[j] - ( *it ) ) - d ) ) );}for( int j : MS[i] ) pos.insert( x[j] );if( minn.w == inf ) continue;else edge.push_back( minn );}}for( int i = 0;i < edge.size();i ++ ) if( merge( edge[i].u, edge[i].v ) ) {-- cnt;G[edge[i].u].push_back( make_pair( edge[i].v, edge[i].w ) );G[edge[i].v].push_back( make_pair( edge[i].u, edge[i].w ) );}}dfs( s, 0, 0 );while( Q -- ) {int i, k;scanf( "%d %d", &i, &k );if( ans[i] <= k ) printf( "Yes\n" );else printf( "No\n" );}return 0;
}

Educational-Round-106

C. Minimum Grid Path

先往上走和先往右走是等价的,因为两个方向上路径综合是相同的nnn(假设先往右走)

直接枚举有多少段,按奇偶分开算,那么有⌈i2⌉\lceil\frac{i}{2}\rceil⌈2i​⌉段都是向右走,⌊n2⌋\lfloor\frac{n}{2}\rfloor⌊2n​⌋都是向上走

贪心的有,花费最小的尽可能多走长度,其余的花费就只让走一步就拐即可

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 100005
int T, n;
int c[maxn], ans[maxn], sum[maxn];signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &c[i] );sum[1] = c[1], sum[2] = c[2];for( int i = 3;i <= n;i ++ )sum[i] = sum[i - 2] + c[i];int minn = c[1]; ans[1] = c[1] * n;for( int i = 3;i <= n;i += 2 )if( c[i] > minn )ans[i] = ans[i - 2] - minn + c[i];else {ans[i] = sum[i - 2] + c[i] * ( n - i / 2 );minn = c[i];}minn = c[2]; ans[2] = c[2] * n;for( int i = 4;i <= n;i += 2 )if( c[i] > minn )ans[i] = ans[i - 2] - minn + c[i];else {ans[i] = sum[i - 2] + c[i] * ( n - i / 2 + 1 );minn = c[i];}int ret = 1e18;for( int i = 2;i <= n;i ++ )ret = min( ret, ans[i] + ans[i - 1] );printf( "%lld\n", ret );}return 0;
}

D. The Number of Pairs

  • 令a=Ag,b=Bg⇒(A,B)=1a=Ag,b=Bg\Rightarrow (A,B)=1a=Ag,b=Bg⇒(A,B)=1,改写式子→c∗A∗B∗g=x+d∗g\rightarrow c*A*B*g=x+d*g→c∗A∗B∗g=x+d∗g

    • 若要等式存在,第一个要满足的条件就是g∣xg\bigg|xg∣∣∣∣​x

      • O(x)O(\sqrt{x})O(x​)预处理出xxx的所有因数
    • 枚举因数ggg
      • 反解出AB=xg+dcAB=\frac{\frac{x}{g}+d}{c}AB=cgx​+d​

        • 同样因为A,BA,BA,B为整数,第二个要满足的条件就是c∣(xg+d)c\bigg|(\frac{x}g{+d)}c∣∣∣∣​(gx​+d)
        • 知道A∗BA*BA∗B后,答案就是A∗BA*BA∗B的2质因子个数2^{质因子个数}2质因子个数,可以通过欧筛提前预处理
#include <cstdio>
#include <vector>
using namespace std;
#define maxn 20000000
int cnt[maxn + 5];
bool vis[maxn + 5];void sieve() {for( int i = 2;i <= maxn;i ++ )if( ! vis[i] )for( int j = i;j <= maxn;j += i )vis[j] = 1, cnt[j] ++;
}int main() {sieve();int T, c, d, x;scanf( "%d", &T );while( T -- ) {scanf( "%d %d %d", &c, &d, &x );vector < int > G;for( int i = 1;i * i <= x;i ++ )if( x % i == 0 ) {G.push_back( i );if( i * i != x ) G.push_back( x / i );}long long ans = 0;for( int i = 0;i < G.size();i ++ ) {int g = G[i];if( ( x / g + d ) % c ) continue;else ans += ( 1ll << cnt[( ( x / g ) + d ) / c] );} printf( "%lld\n", ans );}return 0;
}

E. Chaotic Merge

设dpi,j,k,0/1:dp_{i,j,k,0/1}:dpi,j,k,0/1​: 字符串sss的指针指向iii,字符串ttt的指针指向jjj(si,tjs_i,t_jsi​,tj​还未被操作),是否s,ts,ts,t中的字符都出现过的情况kkk(有444种),合并串当前位是0−si−1/1−tj−10-s_{i-1}/1-t_{j-1}0−si−1​/1−tj−1​的方案数

对应的有四种转移情况,看代码就行

但是题目是要求[l,r][l,r][l,r]区间的,其实从dpi+1,j,1,0=dpi,j+1,2,1=1dp_{i+1,j,1,0}=dp_{i,j+1,2,1}=1dpi+1,j,1,0​=dpi,j+1,2,1​=1的初始化就代表了从i,ji,ji,j开始的字符串转移

#include <cstdio>
#include <cstring>
#define maxn 1005
#define int long long
#define mod 998244353
char s[maxn], t[maxn];
int dp[maxn][maxn][4][2];signed main() {scanf( "%s %s", s, t );int n = strlen( s ), m = strlen( t ), ans = 0;for( int i = 0;i <= n;i ++ )for( int j = 0;j <= m;j ++ ) {if( i < n ) dp[i + 1][j][1][0] = 1;if( j < m ) dp[i][j + 1][2][1] = 1;for( int k = 0;k < 4;k ++ ) {if( 0 < i && i < n && s[i - 1] != s[i] )dp[i + 1][j][k | 1][0] = ( dp[i + 1][j][k | 1][0] + dp[i][j][k][0] ) % mod;if( 0 < j && i < n && t[j - 1] != s[i] )dp[i + 1][j][k | 1][0] = ( dp[i + 1][j][k | 1][0] + dp[i][j][k][1] ) % mod;if( 0 < i && j < m && s[i - 1] != t[j] )dp[i][j + 1][k | 2][1] = ( dp[i][j + 1][k | 2][1] + dp[i][j][k][0] ) % mod;if( 0 < j && j < m && t[j - 1] != t[j] )dp[i][j + 1][k | 2][1] = ( dp[i][j + 1][k | 2][1] + dp[i][j][k][1] ) % mod;}ans = ( ans + dp[i][j][3][0] + dp[i][j][3][1] ) % mod;}printf( "%lld\n", ans );return 0;
}

F. Diameter Cuts

设dpu,i:udp_{u,i}:udpu,i​:u子树内经过uuu的最长路径为iii,枚举每一个儿子vvv转移

  • 断掉与儿子的边,枚举儿子的最长路径jjj

    dpu,i=∑j≤idpu,i⋅dpv,jdp_{u,i}=\sum_{j\le i}dp_{u,i}·dp_{v,j}dpu,i​=∑j≤i​dpu,i​⋅dpv,j​

  • 不断边,那么就是uuu某两个儿子最长路径拼起来

    dpu,i+j+1=∑j(i+j+1≤k)dpu,i⋅dpv,jdp_{u,i+j+1}=\sum_{j(i+j+1\le k)}dp_{u,i}·dp_{v,j}dpu,i+j+1​=∑j(i+j+1≤k)​dpu,i​⋅dpv,j​

每次转移枚举到儿子最长路径即可,时间复杂度O(n2)O(n^2)O(n2)

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 5005
vector < int > G[maxn];
int n, k;
int dp[maxn][maxn];int dfs( int u, int fa ) {dp[u][0] = 1;int dep = 0, dep_son;for( int v : G[u] ) {if( v == fa ) continue;else dep_son = dfs( v, u );vector < int > MS( max( dep, dep_son + 1 ) + 1 );for( int i = 0;i <= dep;i ++ )for( int j = 0;j <= dep_son;j ++ ) {if( i + j + 1 <= k )  MS[max( i, j + 1 )] = ( MS[max( i, j + 1 )] + dp[u][i] * dp[v][j] ) % mod;if( i <= k && j <= k )MS[i] = ( MS[i] + dp[u][i] * dp[v][j] ) % mod;}dep = max( dep, dep_son + 1 );for( int i = 0;i <= dep;i ++ )dp[u][i] = MS[i];}return dep;
}signed main() {scanf( "%lld %lld", &n, &k );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 );int ans = 0;for( int i = 0;i <= k;i ++ )ans = ( ans + dp[1][i] ) % mod;printf( "%lld\n", ans );return 0;
}

G. Graph Coloring

图一定是若干个偶环和若干条路径构成在一起

最小解的构造相当于对于一条路径/环的染色就是欧拉路径/回路的黑白染色法

环肯定可以黑白染色,删去,剩下了一张森林网络

对于一条路径而言,黑白交替染色后就是两个端点产生了贡献(度数为111的点)

因为强制在线那么就只能真的加边,考虑在若干条路径间的加边情况

  • 两个点都不是端点

    那么直接两个点产生了一条新的路径

  • 只有一个点是端点

    直接把另一个点接上端点成为路径的新端点

  • 两个点都是端点

    但此时要注意两个端点的边染色是否相同,必须相同两个端点间的连边才能染成相反的颜色

    如果不同,直接翻转某一个端点的路径即可,个数是不会发生变化的

用sumx,0/isum_{x,0/i}sumx,0/i​表示0−blue/1−red0-blue/1-red0−blue/1−red的xxx路径hashhashhash答案

上述染色问题的过程,带权并查集简直是专攻

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define int long long
#define mod 998244353
#define maxn 400005
vector < int > cnt;
int n, m, Q, ans;
int f[maxn], c[maxn], to[maxn], bit[maxn];
int sum[maxn][2];int find( int x ) {if( f[x] == x ) return x;if( f[f[x]] == f[x] ) return f[x];int fa = find( f[x] );c[x] ^= c[f[x]];return f[x] = fa;
}int color( int x ) {if( x == f[x] ) return c[x];int fa = find( x );return c[x] ^ c[fa];
}void reverse( int x ) {x = find( x );ans = ( ans - sum[x][c[x]] + mod ) % mod;c[x] ^= 1;ans = ( ans + sum[x][c[x]] ) % mod;
}void merge( int x, int y ) {x = find( x ), y = find( y );if( x == y ) return;ans = ( ans - sum[x][c[x]] + mod ) % mod;ans = ( ans - sum[y][c[y]] + mod ) % mod;c[x] ^= c[y], f[x] = y;sum[y][0] = ( sum[y][0] + sum[x][c[x]] ) % mod;sum[y][1] = ( sum[y][1] + sum[x][! c[x]] ) % mod;ans = ( ans + sum[y][c[y]] ) % mod;
}void link( int x, int y, int id ) {sum[id][1] = bit[id] = ( bit[id - 1] << 1 ) % mod, f[id] = id;//起初默认id边颜色为blue(0)if( ! to[x] && ! to[y] ) {//两个都不是端点 新增一条路径reverse( id );to[x] = to[y] = id;}else if( ! to[x] || ! to[y] ) {//只有一个端点直接连起来if( ! to[x] ) swap( x, y );if( ! color( to[x] ) ) reverse( id );//端点边颜色为blue那么新连边就为red(1)merge( to[x], id );to[x] = 0, to[y] = id;}else {//两个都是端点if( color( to[x] ) != color( to[y] ) ) reverse( to[x] );
//必须两端点边颜色相同 其之间的两边才能是另一种颜色 否则需要先翻转其中一条路径if( ! color( to[x] ) ) reverse( id );merge( to[x], id ), merge( to[y], id );to[x] = to[y] = 0;}
}signed main() {int n1, n2; bit[0] = 1;scanf( "%lld %lld %lld", &n1, &n2, &m );for( int i = 1, u, v;i <= m;i ++ ) {scanf( "%lld %lld", &u, &v );link( u, v + n1, i );}scanf( "%lld", &Q );while( Q -- ) {int opt, u, v;scanf( "%lld", &opt );if( opt & 1 ) {scanf( "%lld %lld", &u, &v );link( u, v + n1, ++ m );printf( "%lld\n", ans );}else {cnt.clear();for( int i = 1;i <= m;i ++ )if( color( i ) ) cnt.push_back( i );printf( "%lld ", cnt.size() );for( int i = 0;i < cnt.size();i ++ )printf( "%lld ", cnt[i] );printf( "\n" );}fflush( stdout );}return 0;
}

AtCoder

ARC-122

A - Many Formulae

考虑从前/从后递推到iii时,iii前面符号为+/-的情况数,乘上相应的代价即可

#include <cstdio>
#define int long long
#define mod 1000000007
#define maxn 100005
int n;
int a[maxn];
int f[maxn][2], g[maxn][2];signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &a[i] );f[1][1] = 1;for( int i = 2;i <= n;i ++ ) {f[i][0] = f[i - 1][1];f[i][1] = ( f[i - 1][0] + f[i - 1][1] ) % mod;}g[n][0] = g[n][1] = 1;for( int i = n - 1;i;i -- ) {g[i][0] = g[i + 1][1];g[i][1] = ( g[i + 1][0] + g[i + 1][1] ) % mod;}g[1][0] = 0;int ans = 0;for( int i = 1;i <= n;i ++ )ans = ( ans - a[i] * f[i][0] % mod * g[i][0] % mod + a[i] * f[i][1] % mod * g[i][1] % mod + mod ) % mod;printf( "%lld\n", ans );return 0;
}

B - Insurance

扑面而来的三分气息

看题目描述不可能是二分,看看式子,更加觉得三分有理

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 100005
#define eps 1e-7
int n;
double A[maxn];double check( double x ) {double ans = 0;for( int i = 1;i <= n;i ++ )ans += x + A[i] - min( A[i], x * 2 );return ans / n;
}int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%lf", &A[i] );double l = 0, r = 1e9;while( r - l > eps ) {double mid1 = l + ( r - l ) / 3, mid2 = r - ( r - l ) / 3;if( check( mid1 ) > check( mid2 ) ) l = mid1;else r = mid2;}printf( "%.10f\n", check( l ) );return 0;
}

C - Calculator

果然是套斐波拉契,看操作就能发现端倪,更全面的应该是斐波拉契套倍增

#include <stack>
#include <cstdio>
#include <vector>
using namespace std;
#define int long long
stack < int > st;
vector < int > ans;
int fib[100];
int n;signed main() {scanf( "%lld", &n );fib[1] = fib[2] = 1;for( int i = 3;i <= 88;i ++ )fib[i] = fib[i - 1] + fib[i - 2];for( int i = 88;i;i -- )if( n >= fib[i] ) st.push( i ), n -= fib[i];int ip = 0;while( ! st.empty() ) {int x = st.top(); st.pop();if( x & 1 ) {while( ip < x - 1 ) {if( ip & 1 ) ans.push_back( 4 );else ans.push_back( 3 );ip ++;}ans.push_back( 1 );}else {while( ip < x - 1 ) {if( ip & 1 ) ans.push_back( 4 );else ans.push_back( 3 );ip ++;}ans.push_back( 2 );}}printf( "%lld\n", ans.size() );for( int i = ans.size() - 1;~ i;i -- )printf( "%lld\n", ans[i] );
}

D - XOR Game

显然是跟二进制挂钩

想最后异或的答案最小,换言之贪心二进制从高位到低位顺次考虑尽可能使高位为000

那么就需要0−0,1−10-0,1-10−0,1−1,除非迫不得已再0−10-10−1从而导致该位产生贡献

而这迫不得已的情况就是该位为000(111)的数字个数为奇数时,就必定会产生一条0−10-10−1的配对

否则按0−0,1−10-0,1-10−0,1−1配对方式将数划分成两堆各自重复上述过程

显然这个应该放在字典树上去搞,套着递归求解

#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 200005
int cnt = 1, n, ret;
int siz[maxn * 30], trie[maxn * 30][2];void insert( int x ) {int now = 1;siz[now] ++;for( int i = 29;~ i;i -- ) {int k = x >> i & 1;if( ! trie[now][k] ) trie[now][k] = ++ cnt;now = trie[now][k];siz[now] ++;}
}int work( int x1, int x2, int d ) {int ans = 1 << 30;if( ! x1 ) return ans;if( ! d ) return 0;for( int i = 0;i < 2;i ++ )if( trie[x2][i] ) ans = min( ans, work( trie[x1][i], trie[x2][i], d - 1 ) );elseans = min( ans, work( trie[x1][i], trie[x2][i ^ 1], d - 1 ) + ( 1 << d - 1 ) );return ans;
}void solve( int x, int d ) {if( ! x ) return;if( siz[trie[x][0]] & 1 )ret = max( ret, work( trie[x][0], trie[x][1], d - 1 ) + ( 1 << d - 1 ) );else {solve( trie[x][0], d - 1 );solve( trie[x][1], d - 1 );}
}int main() {scanf( "%d", &n );for( int i = 1, x;i <= ( n << 1 );i ++ ) {scanf( "%d", &x );insert( x );}solve( 1, 30 );printf( "%d\n", ret );return 0;
}

E - Increasing LCMs

要想lcmlcmlcm单增,那么每次的最后数字AiA_iAi​一定是非常特殊的,它满足拥有一个[1,i)[1,i)[1,i)都没有的质因子的幂

贪心的有每次选择这样特殊的数字扔到最后,显然扔的数字越多,剩下的数成为特殊数字的可能性越大

直接暴力模拟选取

#include <cstdio>
#include <iostream>
using namespace std;
#define int long long
#define maxn 105
int n;
int A[maxn], id[maxn];
int d[maxn][maxn];int gcd( int x, int y ) {if( x < y ) swap( x, y );if( ! y ) return x;else return gcd( y, x % y );
}int lcm( int x, int y ) {return x / gcd( x, y ) * y;
}signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &A[i] ), id[i] = i;for( int i = 1;i <= n;i ++ )for( int j = i + 1;j <= n;j ++ )d[i][j] = d[j][i] = gcd( A[i], A[j] );for( int i = n;i;i -- ) {bool flag = 0;for( int j = 1;j <= i;j ++ ) {int g = 1;for( int k = 1;k <= i;k ++ )if( j == k ) continue;else g = lcm( g, d[id[j]][id[k]] );if( g == A[id[j]] ) continue;else {for( int k = j;k < i;k ++ )swap( id[k], id[k + 1] );flag = 1;break;}}if( ! flag ) return ! printf( "No\n" );}printf( "Yes\n" );for( int i = 1;i <= n;i ++ )printf( "%lld ", A[id[i]] );return 0;
}

ABC-206

A - Maxi-Buying

翻译题面,条件判断即可

B - Savings

x(x−1)2≥n,O(n)\frac{x(x-1)}{2}\ge n,O(\sqrt{n})2x(x−1)​≥n,O(n​)枚举xxx即可

C - Swappable

O(n)O(n)O(n)线性,边输入边做,mapmapmap统计xxx出现次数,用已统计的个数减去出现次数即可

D - KAIBUNsyo

直接扫,不对应随便修改其中一个即可,因为修改需要传递,直接并查集维护即可

E - Divide Both

做过一道非常相似的莫比乌斯反演,求gcd(x,y)=1,a≤x≤b,c≤y≤dgcd(x,y)=1,a\le x\le b,c\le y\le dgcd(x,y)=1,a≤x≤b,c≤y≤d的数对个数

拆分成S(b,d)−S(a,d)−S(b,c)+S(a,c)S(b,d)-S(a,d)-S(b,c)+S(a,c)S(b,d)−S(a,d)−S(b,c)+S(a,c)

其中S(n,m):gcd(x,y)=1,1≤x≤n,1≤y≤mS(n,m):gcd(x,y)=1,1\le x\le n,1\le y\le mS(n,m):gcd(x,y)=1,1≤x≤n,1≤y≤m的数对个数

对于此题,先发制人枚举gcdgcdgcd,转化为求gcd(x,y)=1,⌈Lg⌉≤x,y≤⌊Rg⌋gcd(x,y)=1,\lceil\frac{L}{g}\rceil\le x,y\le \lfloor\frac{R}{g}\rfloorgcd(x,y)=1,⌈gL​⌉≤x,y≤⌊gR​⌋的数对个数

在最后枚举i∈[L,R]i∈[L,R]i∈[L,R]减去其因子个数cntcnt_{}cnt​和其倍数Ri\frac{R}{i}iR​

#pragma GCC optimize(2)
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define int long long
#define maxn 1000005
vector < int > d[maxn];
int cnt;
bool vis[maxn];
int prime[maxn], mu[maxn], pre[maxn];void init( int n ) {mu[1] = 1;    for( int i = 2;i <= n;i ++ ) {if( ! vis[i] ) prime[++ cnt] = i, mu[i] = -1;for( int j = 1;j <= cnt && i * prime[j] <= n;j ++ ) {vis[i * prime[j]] = 1;if( i % prime[j] == 0 ) {mu[i * prime[j]] = 0;break;}mu[i * prime[j]] = -mu[i];}}for( int i = 1;i <= n;i ++ ) pre[i] = pre[i - 1] + mu[i];
}int calc( int n, int m ) {if( n > m ) swap( n, m );int last, ans = 0;for( int i = 1;i <= n;i = last + 1 ) {last = min( n / ( n / i ), m / ( m / i ) );ans += ( pre[last] - pre[i - 1] ) * ( n / i ) * ( m / i );}return ans;
}signed main() {int L, R;scanf( "%lld %lld", &L, &R );init( R );int ans = 0;for( int k = 2;k <= R;k ++ ) {int l = ( L - 1 ) / k, r = R / k;ans += calc( r, r ) - calc( l, r ) - calc( r, l ) + calc( l, l );}for( int i = 2;i <= R;i ++ )for( int j = i << 1;j <= R;j += i )d[j].push_back( i );for( int i = max( 2ll, L );i <= R;i ++ ) {//1压根没有被前面莫比乌斯反演算进去 所以不能枚举 枚举了反而可能会让答案少nfor( int j = d[i].size() - 1;~ j;j -- )if( d[i][j] >= L ) ans --;else break;ans -= ( R / i );}printf( "%lld\n", ans );return 0;
}

F - Interval Game 2

SGSGSG函数一学就是一万年,嘤嘤嘤(╥╯^╰╥)

直接送个链接

#include <map>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 105
int T, n;
int L[maxn], R[maxn];
int dp[maxn][maxn];int dfs( int l, int r ) {if( l >= r ) return 0;if( ~ dp[l][r] ) return dp[l][r];map < int, bool > vis;for( int i = 1;i <= n;i ++ )if( l <= L[i] && R[i] <= r )vis[dfs( l, L[i] ) ^ dfs( R[i], r )] = 1;for( int i = 0;;i ++ )if( ! vis[i] ) return dp[l][r] = i;
}int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%d %d", &L[i], &R[i] );memset( dp, -1, sizeof( dp ) );if( dfs( 1, 100 ) ) printf( "Alice\n" );else printf( "Bob\n" );}return 0;
}

ABC-209

C - Not Equal

对于i,i+1i,i+1i,i+1如果i+1i+1i+1的上限比iii大,那么iii无论怎么选,i+1i+1i+1的选择都是ai+1−1a_{i+1}-1ai+1​−1

从这里得到性质,若[1,i][1,i][1,i]是不下降序列,则iii的选择为ai−(i−1)a_i-(i-1)ai​−(i−1),求乘积即可

恰好此题只需要排个序就能满足上述性质

#include <cstdio>
#include <algorithm>
using namespace std;
#define mod 1000000007
#define int long long
#define maxn 200005
int n, ans;
int c[maxn];signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &c[i] );sort( c + 1, c + n + 1 );ans = 1;for( int i = 1;i <= n;i ++ )ans = ans * ( c[i] - i + 1 ) % mod;printf( "%lld\n", ans );return 0;
}

D - Collision

树,边权相同,相同速度从两个点朝对方移动

完全就是树上lcalcalca标配,直接求两个点之间距离的奇偶就知道是在路上还是点上

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 200005
vector < int > G[maxn];
int n, Q;
int dep[maxn];
int f[maxn][20];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];for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( v == fa ) continue;else dfs( v, u );}
}int GetLca( 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 main() {int n, Q, u, v;scanf( "%d %d", &n, &Q );for( int i = 1;i < n;i ++ ) {scanf( "%d %d", &u, &v );G[u].push_back( v );G[v].push_back( u );}dfs( 1, 0 );while( Q -- ) {scanf( "%d %d", &u, &v );int lca = GetLca( u, v );if( ( dep[u] + dep[v] - ( dep[lca] << 1 ) ) & 1 )printf( "Road\n" );elseprintf( "Town\n" );}return 0;
}

E - Shiritori

是个标准的图上博弈问题。

将一个单词看成图上的一条边,首三个字母向末三个字母连一条有向边(哈希或者map编号)

  • 对于没有出边的顶点,状态为N(必胜态)——初始化局面

  • 对于一个没有确定态的顶点

    • 如果有一条出边指向P(必败态),则该顶点为N
    • 如果所有出边都是N,则该顶点为P

用类拓扑方法,确定状态点才会被放进队列,最后若还是未知态则是Draw局面

发现,初始局面确定状态的点进行转移,需要知道其前驱(指向他的多个点)

因此图上博弈问题建的是反图,初始局面的已知态点变成了反图中没有入度的点

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 200005
queue < int > q;
pair < int, int > edge[maxn];
vector < int > G[maxn];
int n;
char s[10];
int ans[maxn], out_d[maxn];int num( char x ) {if( x >= 'A' && x <= 'Z' ) return x - 'A';else return x - 'a' + 26;
}int Hash( char i, char j, char k ) {return num( k ) + 52 * num( j ) + 52 * 52 * num( i );
}int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%s", s + 1 );int len = strlen( s + 1 );edge[i] = make_pair( Hash( s[1], s[2], s[3] ), Hash( s[len - 2], s[len - 1], s[len] ) );out_d[edge[i].first] ++;G[edge[i].second].push_back( edge[i].first );}for( int i = 0;i < 53 * 53 * 53;i ++ )if( ! out_d[i] ) {q.push( i );ans[i] = 1;}while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = 0;i < G[u].size();i ++ ) {int v = G[u][i];if( ! ans[v] ) {-- out_d[v];if( ans[u] == 1 ) ans[v] = -1, q.push( v );else if( ! out_d[v] ) ans[v] = 1, q.push( v );}}}for( int i = 1;i <= n;i ++ )switch( ans[edge[i].second] ) {case 1 : {printf( "Takahashi\n" );break;}case -1 : {printf( "Aoki\n" );break;}case 0 : {printf( "Draw\n" );break;}}return 0;
}

F - Deforestation

考虑相邻两棵树i−1,ii-1,ii−1,i,如果先砍i−1i-1i−1再砍iii,贡献为hi−1+hi+hih_{i-1}+h_i+h_ihi−1​+hi​+hi​;如果先砍iii再砍i−1i-1i−1,贡献为hi+hi−1+hi−1h_i+h_{i-1}+h_{i-1}hi​+hi−1​+hi−1​

显然,后砍的树贡献次数要多一次

所以我们贪心地得出先砍较高的树

设dpi,j:dp_{i,j}:dpi,j​:所有排列中使得iii树是第jjj个被砍掉的数量(插头dpdpdp)

  • hi=hi−1h_i=h_{i-1}hi​=hi−1​,则无所谓先后

    dpi,j=∑k=1i−1dpi−1,kdp_{i,j}=\sum_{k=1}^{i-1}dp_{i-1,k}dpi,j​=∑k=1i−1​dpi−1,k​

  • hi>hi−1h_i>h_{i-1}hi​>hi−1​

    dpi,j=∑k=ji−1dpi−1,kdp_{i,j}=\sum_{k=j}^{i-1}dp_{i-1,k}dpi,j​=∑k=ji−1​dpi−1,k​

  • hi<hi−1h_i<h_{i-1}hi​<hi−1​

    dpi,j=∑k=1j−1dpi−1,kdp_{i,j}=\sum_{k=1}^{j-1}dp_{i-1,k}dpi,j​=∑k=1j−1​dpi−1,k​

#include <cstdio>
#define maxn 4005
#define int long long
#define mod 1000000007
int n;
int h[maxn];
int dp[maxn][maxn];signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &h[i] );h[0] = h[1], dp[0][0] = 1;for( int i = 1;i <= n;i ++ ) {for( int j = 1;j <= i;j ++ ) {if( h[i] == h[i - 1] )dp[i][j] = dp[i - 1][i - 1];else if( h[i] > h[i - 1] )dp[i][j] = ( dp[i - 1][i - 1] - dp[i - 1][j - 1] + mod ) % mod;elsedp[i][j] = dp[i - 1][j - 1];}for( int j = 1;j <= i;j ++ )dp[i][j] = ( dp[i][j] + dp[i][j - 1] ) % mod;}printf( "%lld\n", dp[n][n] );//已经是dp[n][1~n]的前缀和return 0;
}

luogu

LGR-87(Div.2)

远古档案馆

2x2不无脑爆搜才是没有脑子

#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
int s[3][3], t[3][3];
map < int, int > mp;
int End;int id( int g[3][3] ) {int num = 0;for( int i = 1;i <= 2;i ++ )for( int j = 1;j <= 2;j ++ )num = num * 10 + g[i][j];return num;
}void dfs( int g[3][3] ) {int now = id( g );if( now == End ) {printf( "Yes\n" );exit( 0 );}if( mp[now] ) return;mp[now] = 1;int tmp[3][3] = {};for( int i = 1;i <= 2;i ++ )for( int j = 1;j <= 2;j ++ )tmp[i][j] = g[i][j];for( int i = 1;i <= 2;i ++ )for( int j = 1;j <= 2;j ++ )if( ! tmp[i][j] ) continue;else {if( i > 1 && ! tmp[i - 1][j] ) {swap( tmp[i - 1][j], tmp[i][j] );dfs( tmp );for( int x = 1;x <= 2;x ++ )for( int y = 1;y <= 2;y ++ )tmp[x][y] = g[x][y];}if( j > 1 && ! tmp[i][j - 1] ) {swap( tmp[i][j], tmp[i][j - 1] );dfs( tmp );for( int x = 1;x <= 2;x ++ )for( int y = 1;y <= 2;y ++ )tmp[x][y] = g[x][y];}if( i < 2 && ! tmp[i + 1][j] ) {swap( tmp[i][j], tmp[i + 1][j] );dfs( tmp );for( int x = 1;x <= 2;x ++ )for( int y = 1;y <= 2;y ++ )tmp[x][y] = g[x][y];}if( j < 2 && ! tmp[i][j + 1] ) {swap( tmp[i][j], tmp[i][j + 1] );dfs( tmp );for( int x = 1;x <= 2;x ++ )for( int y = 1;y <= 2;y ++ )tmp[x][y] = g[x][y];}}
}int main() {for( int i = 1;i <= 2;i ++ )for( int j = 1;j <= 2;j ++ )scanf( "%d", &s[i][j] );for( int i = 1;i <= 2;i ++ )for( int j = 1;j <= 2;j ++ )scanf( "%d", &t[i][j] );End = id( t );dfs( s );printf( "No\n" );return 0;
}

珍珠帝王蟹

  • 暴力大讨论多种情况,ε=ε=ε=┏(゜ロ゜;)┛

  • 因为1<|v|,所以肯定是不能放过任何一个乘数的

    • 所有乘数乘积为正

      • 负乘数个数为偶数

        先把加正数的操作做了,再乘所有乘数,最后减去所有负数

      • 负乘数个数为奇数

        先把加正数的操作做了,再乘一个最大的负乘数,然后加上所有负整数操作,最后一起乘剩下乘数

    • 所有乘数乘积为负

      先把负数加在一起,再乘一个最大的负乘数,转化为正数,然后加上所有正数操作,最后乘所有乘数

#include <cstdio>
#define int long long
#define mod 998244353
int n, v, add, add_, mul = 1, t = 1;
char op[5];signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%s %lld", op, &v );if( op[0] == '+' )if( v > 0 ) add = ( add + v ) % mod;else add_ = ( add_ + v ) % mod;else if( t > 0 || ( v > t && v < 0 ) ) mul = mul * t % mod, t = v;else mul = mul * v % mod;}if( mul * t > 0 )if( t > 0 ) printf( "%lld\n", ( add * mul % mod * t % mod + add_ + mod ) % mod );else printf( "%lld\n", ( add * mul % mod * t % mod + add_ * mul % mod + mod ) % mod );elseprintf( "%lld\n", ( add_ * mul % mod * t % mod + add * mul % mod + mod ) % mod );return 0;
}

天体探测仪

  • ∀i\forall i∀i,设[li,ri]:[l_i,r_i]:[li​,ri​]: iii是区间内最小值的最长区间

  • 考虑在[li,ri][l_i,r_i][li​,ri​]中如何确定iii的位置,令len=ri−li+1len=r_i-l_i+1len=ri​−li​+1

    • 对于长度=j= j=j的区间个数为len−j+1len-j+1len−j+1,若所有区间都包含iii,那么iii应该出现len−j+1len-j+1len−j+1次
    • 找到最大的jjj,使得iii出现次数<len−j+1<len-j+1<len−j+1,此时iii的位置可以为li+j/ri−jl_i+j/r_i-jli​+j/ri​−j
  • 在操作过程中,显然只用到了区间的长度lenlenlen,并不关心区间真正是什么样子

  • 巧合的,发现lenlenlen在输入中就已经给出,最大的jjj满足i∈Sj→len=ji∈S_{j}\rightarrow len=ji∈Sj​→len=j

  • 用队列qiq_iqi​维护当前长度为iii的空区间,队列内的每个元素代表一个区间的左端点

    初始化qnq_nqn​存了[1,n][1,n][1,n]整个序列,然后从111到nnn开始放数

    每次取出qlenq_{len}qlen​的队头,将区间裂成len′=j,len′′=len−j−1len'=j,len''=len-j-1len′=j,len′′=len−j−1

#include <queue>
#include <cstdio>
using namespace std;
#define maxn 805
queue < int > q[maxn];
int n;
int last[maxn], ans[maxn];
int cnt[maxn][maxn];int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )for( int j = 1, k;j <= n - i + 1;j ++ ) {scanf( "%d", &k );cnt[k][i] ++, last[k] = i;}q[n].push( 1 );for( int i = 1;i <= n;i ++ ) {int pos = 0;for( int j = last[i] - 1;j;j -- )if( cnt[i][j] < cnt[i][last[i]] + last[i] - j ) {pos = j;break;}int l = q[last[i]].front();//last[i]就是len(i) q[last[i]].pop();ans[l + pos] = i;q[pos].push( l );q[last[i] - pos - 1].push( l + pos + 1 );//放的是新区间的左端点L }for( int i = 1;i <= n;i ++ )printf( "%d ", ans[i] );return 0;
}

[CF/AT/Luogu]各大网站网赛 爆肝部部长工作报告文件Ⅱ相关推荐

  1. [CF/AT]各大网站网赛 体验部部长第一季度工作报告

    文章目录 CodeForces #712 (Div. 1)--1503 A. Balance the Bits B. 3-Coloring C. Travelling Salesman Problem ...

  2. 沙发家具网站源码_小户型装修不会选家具?大湾网推荐你了解这些装修风格家具,装修省心空间大!...

    原标题:小户型装修不会选家具?大湾网推荐你了解这些装修风格家具,装修省心空间大! 家具与环境和谐统一,美式.中式.欧式.田园,风格迥异,大有不同,你到底适合什么样的装修风格?帮你测一测吧! 现代简约风 ...

  3. 09年关门歇业的15大网站 雅虎旗下4网站上榜

    昨日,据国外媒体报道,美国IT网站Cnet日前评出了2009年关闭的15大网站.在这些网站当中,包括了4个来自雅虎旗下的网站.以下为文章内容摘要: 2009年涌现出了许多具有天才般创意的新网站,当然受 ...

  4. Web2.0时代最值得网民们关注的十大网站(2006年)

    Web2.0时代最值得网民们关注的十大网站 http://www.sina.com.cn 2006年09月20日 17:37 赛迪网 在Web2.0革命浪的潮中,技术与创新成为互联网的灵魂.虽然目前国 ...

  5. 2014年各大网站编辑/主编薪资大曝光

    新浪新闻频道每个岗位的工资情况: 新浪新闻频道人数多,底子大,工资待遇起点不低. 目前,发钱的职位有:实习生.普通编辑.高级编辑.副主编.主编.总监 他们的每个岗位工资(平均)是: 实习生 1500- ...

  6. GitHub 遭黑客攻击勒索;苹果夸大 iPhone 电池续航时间;全球第二大暗网被摧毁 | 极客头条...

    快来收听极客头条音频版吧,智能播报由标贝科技提供技术支持. 「CSDN 极客头条」,是从 CSDN 网站延伸至官方微信公众号的特别栏目,专注于一天业界事报道.风里雨里,我们将每天为朋友们,播报最新鲜有 ...

  7. 计算机专业论文选题网站方面,5大网站汇总,搞定新颖的计算机专业毕业设计网站汇总...

    原标题:5大网站汇总,搞定新颖的计算机专业毕业设计网站汇总 2021年了,很多计算机专业的同学都会问,我不想再做XX管理系统.XX选课系统了,哪里有一些新颖的毕业设计题目可以参考?或者做新颖的毕业设计 ...

  8. 关于如何使用python下载各大网站的视频

    关于如何使用python下载各大网站的视频 前提摘要: 最近有些朋友问我怎么下载例如腾讯爱奇艺这些网站的视频,众所周知,这些网站视频只能先在PC端下载他们的客户端,然后再在客户端上缓存,而且缓存下来的 ...

  9. 【2021年第三届全国高校计算机能力挑战赛】大数据应用赛

    [2021年第三届全国高校计算机能力挑战赛]大数据应用赛 赛题:大数据应用赛 一.赛题背景: 二.时间安排: 三.奖项设置: 四.赛题和数据: 五.评价标准: 六.作品提交要求: 七.解决方案: 八. ...

最新文章

  1. [leetcode-61-Rotate List]
  2. java如何设置圆角边框_巧妙实现带圆角的渐变边框
  3. 前端学习(2324):angular初步使用
  4. QGraphicsProxyWidget paintEvent(from 1+1 =2)
  5. html word classid,html之object标签的classid收集
  6. 关系型数据库的隔离级别 读一致性
  7. 小乌龟git的安装、配置(TortoiseGit安装、配置)
  8. CentOS 7 安装 jdk11
  9. 网页右下角弹出窗体实现代码
  10. 联想win7无法连接无线网络连接服务器,联想笔记本连不上wifi该怎么处理
  11. 天啦噜!知道硬盘很慢,但没想到比 CPU L1 Cache 慢 10000000 倍
  12. 论用户体验测试:牛逼的功能千篇一律,好的用户体验万里挑一
  13. 基于RSA+AES 软件授权License
  14. 扩展odoo res.partner的问题
  15. 虚拟主机安装php网站教程,虚拟主机简单安装wordpress教程
  16. C语言的除法与取余运算
  17. 华为:活下去,是企业的硬道理
  18. 盼望着,盼望着,东风来了,春天的脚步近了。
  19. 如何零基础自学平面广告设计
  20. Microsoft .NET Framework 4.0安装时发生严重错误 无法安装

热门文章

  1. idea mysql 创建表_idea 根据数据库表自动创建持久化类
  2. python 多分类情感_python 文本情感分类
  3. linux 文档属于apache,Apache 安装和使用文档
  4. ios支付宝支付失败不回调_iOS 支付宝网页支付回调问题
  5. 信号转化java_Java基础知识回顾-7
  6. 禁用笔记本键盘_如何禁用/启用笔记本内置键盘?
  7. 2023届春招实习拉钩一面凉经
  8. 2019年第十届蓝桥杯国赛B组试题G-排列数-next_permutation枚举,模拟
  9. 算法-排序-k排序(算法导论第三版第八章思考题8-5)
  10. 01tire+洛谷P4551 最长异或路径