A - Good morning
题意:
语法题:
给定两个起床时间。
若第一个早于等于第二个,输出T a k a h a s h i {\color{Red}Takahashi } T a k a h a s h i ,
反之输出A o k i {\color{Red}Aoki} A o k i 。
思路:
见代码
时间复杂度:
O ( 1 ) O(1) O ( 1 )
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <bits/stdc++.h> typedef long long ll;const int N = 1e2 +10 ,M = N * 2 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int a,b,c,d; std::cin>>a>>b>>c>>d; if (a*60 +b<=c*60 +d)std::cout<<"Takahashi" ; else std::cout<<"Aoki" ; return 0 ; }
B - Mex
题意:
给定一个长度为n的序列,输出这个序列中未出现的最小自然数。
思路:
用一个数组统计数字出现次数,然后寻找这个序列中未出现的最小自然数。
时间复杂度:
O ( n ) O(n) O ( n )
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> typedef long long ll;const int N = 2e3 +10 ,M = N * 2 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;int cnt[N];int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int n; std::cin>>n; for (int i = 1 ; i <= n ; i++) { int x; std::cin>>x; cnt[x]++; } for (int i = 0 ; ; i++) { if (!cnt[i]) { std::cout<<i; return 0 ; } } return 0 ; }
C - Choose Elements
题意:
给定两个序列和一个整数k:
a 1 , a 2 … a n − 1 , a n a_{1},a_{2}\dots a_{n-1},a_{n} a 1 , a 2 … a n − 1 , a n
b 1 , b 2 … b n − 1 , b n b_{1},b_{2}\dots b_{n-1},b_{n} b 1 , b 2 … b n − 1 , b n
要求构造出一个序列:
c 1 , c 2 … c n − 1 , c n ( c i = a i 或 c i = b i ) c_{1},c_{2}\dots c_{n-1},c_{n}(c_{i}=a_{i}或c_{i}=b_{i}) c 1 , c 2 … c n − 1 , c n ( c i = a i 或 c i = b i )
使得对于c i ( 1 ≤ i ≤ n − 1 ) c_{i}(1\leq i \leq n-1) c i ( 1 ≤ i ≤ n − 1 ) 有:
∣ c [ i ] − c [ i + 1 ] ∣ ≤ k |c[i]-c[i+1]|\leq k ∣ c [ i ] − c [ i + 1 ] ∣ ≤ k
思路:
如果对于 i ( 1 ≤ i ≤ n − 2 ) \ i\ (1\leq i\leq n-2) i ( 1 ≤ i ≤ n − 2 ) 都满足如上要求。
那么显然此时是否满足要求只与a n − 1 , a n , b n − 1 , b n a_{n-1},a_{n},b_{n-1},b_{n} a n − 1 , a n , b n − 1 , b n 有关。
这种性质满足无后效性,考虑使用动态规划。
我们设有d p [ i ] [ 2 ] dp[i][2] d p [ i ] [ 2 ] :
若d p [ i ] [ 0 ] = 0 dp[i][0]=0 d p [ i ] [ 0 ] = 0 表示第a i a_{i} a i 可用
若d p [ i ] [ 0 ] = 1 dp[i][0]=1 d p [ i ] [ 0 ] = 1 表示第a i a_{i} a i 不可用
若d p [ i ] [ 1 ] = 0 dp[i][1]=0 d p [ i ] [ 1 ] = 0 表示第b i b_{i} b i 可用
若d p [ i ] [ 1 ] = 1 dp[i][1]=1 d p [ i ] [ 1 ] = 1 表示第b i b_{i} b i 不可用
(这里可能用1表示可用更好,但是当时我不想初始化)
即可以按如下方式转移:
1 2 3 4 5 6 7 for (int i = 2 ; i <= n ; i++) { if (!dp[i-1 ][0 ]&&abs (a[i]-a[i-1 ])<=k||!dp[i-1 ][1 ]&&abs (a[i]-b[i-1 ])<=k)dp[i][0 ] = 0 ; else dp[i][0 ] = 1 ; if (!dp[i-1 ][0 ]&&abs (b[i]-a[i-1 ])<=k||!dp[i-1 ][1 ]&&abs (b[i]-b[i-1 ])<=k)dp[i][1 ] = 0 ; else dp[i][1 ] = 1 ; }
那么最后d p [ n ] [ 0 ] dp[n][0] d p [ n ] [ 0 ] 或d p [ n ] [ 1 ] dp[n][1] d p [ n ] [ 1 ] 存在一个可用即可。
时间复杂度:
O(n)
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <bits/stdc++.h> typedef long long ll;const int N = 2e5 +10 ,M = N * 2 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;int a[N],b[N],dp[N][2 ];int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int n,k; std::cin>>n>>k; for (int i = 1 ; i <= n ; i++)std::cin>>a[i]; for (int i = 1 ; i <= n ; i++)std::cin>>b[i]; for (int i = 2 ; i <= n ; i++) { if (!dp[i-1 ][0 ]&&abs (a[i]-a[i-1 ])<=k||!dp[i-1 ][1 ]&&abs (a[i]-b[i-1 ])<=k)dp[i][0 ] = 0 ; else dp[i][0 ] = 1 ; if (!dp[i-1 ][0 ]&&abs (b[i]-a[i-1 ])<=k||!dp[i-1 ][1 ]&&abs (b[i]-b[i-1 ])<=k)dp[i][1 ] = 0 ; else dp[i][1 ] = 1 ; } if (!dp[n][0 ]||!dp[n][1 ])std::cout<<"Yes" ; else std::cout<<"No" ; return 0 ; }
D - Polynomial division
题意:
存在多项式:
a 0 + a 1 x 1 + a 2 x 2 … a n − 1 x n + a n x n a_{0}+a_{1}x^{1}+a_{2}x^{2}\dots a_{n-1}x^{n}+a_{n}x^{n} a 0 + a 1 x 1 + a 2 x 2 … a n − 1 x n + a n x n
b 0 + b 1 x 1 + b 2 x 2 … b m − 1 x m + b m x m b_{0}+b_{1}x^{1}+b_{2}x^{2}\dots b_{m-1}x^{m}+b_{m}x^{m} b 0 + b 1 x 1 + b 2 x 2 … b m − 1 x m + b m x m
将两个多项式相乘得到:
a 0 b 0 + ( b 1 a 0 + b 0 a 1 ) x 1 … a n b m x n x m − > a_{0}b_{0}+(b_{1}a_{0}+b_{0}a_{1})x^{1}\dots a_{n}b_{m}x^{n}x^{m}-> a 0 b 0 + ( b 1 a 0 + b 0 a 1 ) x 1 … a n b m x n x m − >
c 0 + c 1 x 1 … c n + m x n x m c_{0}+c_{1}x^{1}\dots c_{n+m}x^{n}x^{m} c 0 + c 1 x 1 … c n + m x n x m
现在给出所有系数a i a_{i} a i 和c i c_{i} c i ,请输出所有的b i b_{i} b i 。
思路:
开始时,有b [ 0 ] = c [ 0 ] a [ 0 ] b[0]=\frac{c[0]}{a[0]} b [ 0 ] = a [ 0 ] c [ 0 ] ,并做如下处理:
c [ i ] = c [ i ] − b [ 0 ] ∗ a [ i ] ( 0 ≤ i ≤ n ) c[i]=c[i]-b[0]*a[i](0\leq i\leq n) c [ i ] = c [ i ] − b [ 0 ] ∗ a [ i ] ( 0 ≤ i ≤ n ) 。
处理完后显然有b [ 1 ] = c [ 1 ] a [ 0 ] b[1]=\frac{c[1]}{a[0]} b [ 1 ] = a [ 0 ] c [ 1 ] ,并重复上述处理:
c [ i + 1 ] = c [ i + 1 ] − b [ 1 ] ∗ a [ i ] ( 0 ≤ i ≤ n − 1 ) c[i+1]=c[i+1]-b[1]*a[i](0\leq i\leq n-1) c [ i + 1 ] = c [ i + 1 ] − b [ 1 ] ∗ a [ i ] ( 0 ≤ i ≤ n − 1 ) 。
因此我们可以重复上述步骤直至求出所有b j ( 0 ≤ j ≤ n ) b_{j}(0\leq j\leq n) b j ( 0 ≤ j ≤ n ) :
b [ j ] = c [ j ] a [ 0 ] b[j]=\frac{c[j]}{a[0]} b [ j ] = a [ 0 ] c [ j ]
c [ i + j ] = c [ i + j ] − b [ j ] ∗ a [ i ] c[i+j]=c[i+j]-b[j]*a[i] c [ i + j ] = c [ i + j ] − b [ j ] ∗ a [ i ] 。
时间复杂度:
O ( n m ) O(nm) O ( n m )
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> typedef long long ll;const int N = 2e5 +10 ,M = N * 2 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;int a[N],b[N],c[N];int main () { int n,m; std::cin>>n>>m; for (int i = n ; i >= 0 ; i--)std::cin>>a[i]; for (int i = m + n; i >= 0 ; i--) { std::cin>>c[i]; } for (int i = 0 ; i <= m ; i++) { b[i] = c[i]/a[0 ]; for (int j = 1 ; j <= n ; j++) { c[i+j] -= (b[i] * a[j]); } } for (int i = m ; i >= 0 ; i--)std::cout<<b[i]<<" " ; return 0 ; }
E - Wrapping Chocolate
题意:
有n个巧克力,宽度为a i a_{i} a i ,长度为b i b_{i} b i 。
有m个盒子,宽度为c i c_{i} c i ,长度为d i d_{i} d i 。
如果a i ≤ c i , b i ≤ d i a_{i}\leq c_{i},b_{i}\leq d_{i} a i ≤ c i , b i ≤ d i ,那么第i i i 盒子就可以装下第i i i 个巧克力。
询问是否存在一种方案,可以用盒子把所有的巧克力装下。
思路:
没看数据之前,还想着暴力跑二分图最大匹配
无序查找必然超时,考虑对某个关键字排序。
我们把巧克力和盒子一起按照宽度降序排序(如果宽度相同则把盒子排在前面),遍历排序后的数组,并维护一个集合S,按照如下方式处理:
1.如果当前位置为盒子:将长度d i d_{i} d i 加入集合。
2.如果当前位置为巧克力:找到d i ( m i n ( d i ≥ b i ) , d i ∈ S ) d_{i}(min(di\geq b_{i}),d_{i}\in S) d i ( m i n ( d i ≥ b i ) , d i ∈ S ) ,并将d i d_{i} d i 删除,如果查找失败,则不存在方案。
易证明上述操作可以完成题目要求,上述操作可以用multiset完成。
时间复杂度:
O(nlogn)
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <bits/stdc++.h> typedef long long ll;const int N = 4e5 +10 ,M = N * 4 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;struct Node { int x,y; bool box; bool operator <(const Node &a)const { if (a.x == x)return a.box < box; return a.x < x; } }a[N]; std::multiset<int > st; int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int n,m; std::cin>>n>>m; for (int i = 1 ; i <= n ; i++) { int x; std::cin>>x; a[i].x = x; } for (int i = 1 ; i <= n ; i++) { int x; std::cin>>x; a[i].y = x; a[i].box = false ; } for (int i = n + 1 ; i <= m + n; i++) { int x; std::cin>>x; a[i].x = x; } for (int i = n + 1 ; i <= m + n ; i++) { int x; std::cin>>x; a[i].y = x; a[i].box = true ; } std::sort (a+1 ,a+1 +n+m); for (int i = 1 ; i <= n + m; i++) { if (a[i].box) { st.insert (a[i].y); } else { auto it = st.lower_bound (a[i].y); if (it!=st.end ())st.erase (it); else { std::cout<<"No" ; return 0 ; } } } std::cout<<"Yes" ; return 0 ; }
F - Endless Walk
题意:
给定一张有向图,寻找这样的点,满足如下要求:
从这个点出发,存在一条无限长的路径。
输出有几个点满足上述要求。
思路:
满足上述的点存在两种情况:
1.该点在环内
2.从这个点出发可以走到环
我的处理可能复杂些,按照如下步骤:
1.强连通分量跑tarjan缩点,对于任意的强连通分量,若内部点数s i z e i ≥ 2 size_{i}\geq2 s i z e i ≥ 2 ,则这些点都在环内。
2.若选择这样的做法,细节较多:
从其他点开始搜索,尝试是否能走到环。
尝试采用另一种方法:
建立反向图,从环开始搜索,对于所有搜索到的点,都满足要求。
时间复杂度:
O(n+m)
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <bits/stdc++.h> typedef long long ll;const int N = 2e5 +10 ,M = N * 4 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;int h[N],hs[N],e[M],ne[M],idx;int id[N],dfn[N],low[N],stk[N],size[N],scc,top,ts;bool book[N],vis[N],OK[N];int res;void add (int h[],int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx++; } void tarjan (int u) { dfn[u] = low[u] = ++ts; book[u] = true ,stk[++top] = u; for (int i = h[u] ; ~i ; i = ne[i]) { int v = e[i]; if (!dfn[v]) { tarjan (v); low[u] = std::min (low[u],low[v]); } else if (book[v])low[u] = std::min (low[u],dfn[v]); } if (low[u]==dfn[u]) { int temp; scc++; do { temp = stk[top--]; book[temp] = false ; id[temp] = scc; size[scc]++; }while (temp!=u); } } void dfs (int u) { res++,vis[u] = OK[u] = true ; for (int i = hs[u] ; ~i ; i = ne[i]) { int v = e[i]; if (vis[v])continue ; dfs (v); } } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); memset (h,-1 ,sizeof h); memset (hs,-1 ,sizeof hs); int n,m; std::cin>>n>>m; while (m--) { int a,b; std::cin>>a>>b; add (h,a,b),add (hs,b,a); } for (int i = 1 ; i <= n ; i++)if (!dfn[i])tarjan (i); for (int i = 1 ; i <= n ; i++) if (size[id[i]] > 1 )OK[i] = true ; for (int i = 1 ; i <= n ; i++)if (OK[i]&&!vis[i])dfs (i); std::cout<<res; return 0 ; }
G - Foreign Friends
题意:
现有N个人,这些人属于K个国家,这N个人中有L个人是受欢迎的人,这L个人分别属于不同的国家。
小明是个中间人,存在M对关系:
小明可以让某一对中的两个人相互认识,但是要花费一定的金额。
对于这N个人中的每一个人a i a_{i} a i ,他们都想要认识一个不在自己国家的受欢迎的人,分别输出这N个人所需的最小花费。
(若a认识b,b认识c,那么有a认识c)
思路:
比较显然的,这是一个最短路问题,可以使用Dijkstra(使用优先队列)。
正向考虑跑最短路显然不行,我们反向考虑让每个受欢迎的人去认识每一个人,但是我们如果让每个受欢迎的人去跑最短路,时间复杂度也会达到O ( L N l o g N ) O(LNlogN) O ( L N l o g N ) ,但是这题比较特殊,我们可以进行如下考虑:
1.我们把所有受欢迎的人放在优先队列里同时跑最短路,并记录他们所属的国家,在这个过程中我们通过记录,保证每个国家只会更新a i a_{i} a i 一次。
2.那么当人a i a_{i} a i 被第一次和第二次取出的时候,所需的花费分别是最小,次小的,并且更新这两次的国家不同。因为a i a_{i} a i 只会属于一个国家,所以这两个最小数中一定有一个和自己的国家不同。
3.如果与更新最小值的国家不同,输出最小值即可,反之输出次小值。
时间复杂度:
O(nlogn)
AC代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <bits/stdc++.h> typedef long long ll;const int N = 2e5 +10 ,M = N * 4 ,INF = 0x3f3f3f3f ,mod = 1e9 +7 ;struct Node { ll dist; int u,city; bool operator <(const Node &a)const { return a.dist < dist; } }; int h[N],e[N],ne[N],w[N],idx;int vis[N],city[N],type[N];ll dist1[N],dist2[N]; int n,m,k,l;std::priority_queue<struct Node> que; void add (int a,int b,int c) { e[idx] = b,ne[idx] = h[a],w[idx] = c,h[a] = idx++; } void Dijkstra () { while (!que.empty ()) { auto temp = que.top (); que.pop (); if (vis[temp.u] < 2 && type[temp.u] != temp.city) { if (vis[temp.u] == 0 ) type[temp.u] = temp.city,dist1[temp.u] = temp.dist; else dist2[temp.u] = temp.dist; vis[temp.u]++; for (int i = h[temp.u] ; ~i ; i = ne[i]) { int v = e[i]; que.push ({temp.dist+w[i],v,temp.city}); } } } } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); memset (h,-1 ,sizeof h); std::cin>>n>>m>>k>>l; for (int i = 1 ; i <= n ; i++)std::cin>>city[i]; for (int i = 1 ; i <= l ; i++) { int x; std::cin>>x; que.push ({0ll ,x,city[x]}); } while (m--) { int a,b,c; std::cin>>a>>b>>c; add (a,b,c),add (b,a,c); } Dijkstra (); for (int i = 1 ; i <= n ; i++) { if (type[i] != city[i]&&dist1[i])std::cout<<dist1[i]<<" " ; else if (dist2[i])std::cout<<dist2[i]<<" " ; else std::cout<<-1 <<" " ; } return 0 ; }