Cheeeeen the Cute Cat 题目大意 给定一个二分图,两边有个点,求二分图的最大匹配。
解题思路 模板题,直接()跑一边二分图即可。
参考代码 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 77 78 79 80 #include <cstdio> #include <queue> #include <vector> #include <cstring> #include <algorithm> #define ri register int using namespace std;#define gc getchar template <class T >void read (T&x) { ri f=1 ,c;while (c=gc (),c<48 ||57 <c)if (c=='-' )f=-1 ;x=c^48 ; while (c=gc (),47 <c&&c<58 )x=(x<<3 )+(x<<1 )+(c^48 );x*=f; } const int N = 1000010 , INF = 0x3f3f3f3f ;vector<int > G[N]; int Nx,Ny,k;int Mx[N],My[N];int dx[N],dy[N];int dis,u,v;bool used[N];bool searchP () { queue<int > Q; dis = INF; memset (dx,-1 ,sizeof (dx)); memset (dy,-1 ,sizeof (dy)); for (int i = 0 ;i < Nx;++i) if (Mx[i] == -1 ) Q.push (i), dx[i] = 0 ; while (!Q.empty ()){ int u = Q.front ();Q.pop (); if (dx[u] > dis) break ; int sz = G[u].size (); for (int i = 0 ;i < sz;++i){ int v = G[u][i]; if (dy[v] == -1 ) { dy[v] = dx[u] + 1 ; if (My[v] == -1 ) dis = dy[v]; else dx[My[v]] = dy[v] + 1 , Q.push (My[v]); } } } return dis != INF; } bool DFS (int u) { int sz = G[u].size (); for (int i = 0 ;i < sz;++i){ int v = G[u][i]; if (!used[v] && dy[v] == dx[u] + 1 ){ used[v] = true ; if (My[v] != -1 && dy[v] == dis) continue ; if (My[v] == -1 || DFS (My[v])){ My[v] = u; Mx[u] = v; return true ; } } } return false ; } int MaxMatch () { int res = 0 ; memset (Mx,-1 ,sizeof (Mx)); memset (My,-1 ,sizeof (My)); while (searchP ()){ memset (used,false ,sizeof (used)); for (int i = 0 ;i < Nx;++i) if (Mx[i] == -1 && DFS (i)) ++res; } return res; } int main () { int n; read (n); Nx=Ny=n; for (int i=1 ;i<=n;++i){ for (int j=1 ;j<=n;++j){ int x; read (x); if (x) G[i-1 ].push_back (j+n-1 ); } } printf ("%d\n" ,MaxMatch ()); }
Cirno’s Perfect Equation Class 题目大意 给定,问你所有满足以下条件的二元组的个数:
解题思路 试除法求约数,求出所有c的约数,然后暴力枚举每个约束是否符合上述条件,符合则计数。
时间复杂度:
参考代码 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 #include <bits/stdc++.h> using namespace std;#define endl '\n' #define debug(a) cout<<#a<<"=" <<a<<endl; #define int long long typedef long long LL;typedef pair<int ,int > PII;typedef pair<double ,double > PDD;const double eps=1e-8 ;bool multi=1 ;vector<int > divs; void get_divisors (int n) { for (int i=1 ;i<=n/i;i++) { if (n%i==0 ){ divs.push_back (i); if (n/i!=i) divs.push_back (n/i); } } } void solve () { divs.clear (); int k,c,n; cin>>k>>c>>n; get_divisors (c); int ans=0 ; for (int i=0 ;i<divs.size ();i++){ int b=divs[i]; if ((c-b)%k!=0 ) continue ; int a=(c-b)/k; if (a==0 ) continue ; if (__gcd(a,b)>=n){ ans++; } } cout<<ans<<endl; } signed main () { ios::sync_with_stdio (false );cin.tie (0 );cout.tie (0 ); int TTT=1 ; if (multi) cin>>TTT; while (TTT--){ solve (); } return 0 ; }
Red and Blue and Green 题目大意 给定m个区间,这些区间要么包含要么不相交,每个区间包含一个表示下方构造的排列区间的逆序对数量的奇偶性为(0/1表示偶/奇)。现在要你构造一组排列,使得满足上述所有区间的限制。
解题思路 (以下奇偶性均指区间逆序对的奇偶性)
因为这个区间要么包含要么不交,所以被包含的区间可以作为大区间的子树,不交的两个区间可以作为一个大区间的两个子树或作为两棵树,即我们可以构造出一个树形结构(去掉重复区间)。
我们可以按照区间大小排序,然后建图对每个小区间找到一个包含它的大区间连一条大区间向小区间的边。
通过对树形结构dfs,利用异或的性质,对于一个大区间的奇偶性异或上所有它的子树的奇偶性后若不为0则不合法,不可能构成符合题意的情况。
首先,最初默认pos[i]=i,逆序对数量为偶,在dfs的过程中,如果这个区间奇偶性异或上所有子树的区间奇偶性后为0,则当前区间可以不需要操作,如:为奇,它的子树为奇,,那么当前区间奇偶性已经满足无需操作。反之不符,我们可以通过交换来使逆序对奇偶性改变。
考虑以下几种情况:
首先,要满足每个子树的奇偶性不变的同时使得大区间的奇偶性改变。那么我们可以分类讨论。
①当子树为空时,直接交换当前区间最小的两个数的位置。
②当子树中最左边的区间的左端点与当前树的区间的左端点重合,交换最左边区间的最大值和右边区间的最小值的位置。(因为这里记录得是值为i的位置在哪,所以直接交换和即可。
③剩下的情况也就是最左边的区间的左端点不与当前树这个大区间的左端点重合时,交换子树最左边区间的最小值的位置和{这个最小值-1}的位置。
参考代码 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 77 78 79 80 81 82 83 84 85 86 #include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std;using ll = long long ;bool multi=0 ;const int N = 1e3 + 10 ;struct Seg { int l, r, k; }; int n, m, a[N], pos[N], deg[N];vector<Seg> s; vector<int > G[N]; map<pair<int , int >, int > mp; void op (int k) { swap (pos[k], pos[k + 1 ]); }void dfs (int u) { int w = s[u].k; sort (G[u].begin (), G[u].end (), [&](int a, int b){ return s[a].l <s[b].l; }); for (auto v: G[u]) { dfs (v); w ^= s[v].k; } if (!w) return ; if (G[u].empty ()) op (s[u].l); else if (s[G[u][0 ]].l == s[u].l) op (s[G[u][0 ]].r); else op (s[G[u][0 ]].l - 1 ); } void solve () { cin >> n >> m; for (int i = 1 ; i <= n; i++){ pos[i] = i, mp[{i, i}] = 0 ; } while (m--){ int l, r, k; cin >> l >> r >> k; if (mp.count ({l, r}) && mp[{l, r}] != k){ cout << -1 << '\n' ; return ; } if (mp.count ({l, r})) continue ; mp[{l, r}] = k; s.push_back ({l, r, k}); } m = s.size (); sort (s.begin (), s.end (),[](Seg a, Seg b) { return a.r - a.l < b.r - b.l; }); for (int i = 0 ; i < m; i++){ for (int j = i + 1 ; j < m; j++){ if (s[j].l <= s[i].l && s[i].r <= s[j].r){ G[j].push_back (i), ++deg[i]; break ; } } } for (int i = 0 ; i < m; i++){ if (!deg[i]) dfs (i); } for (int i = 1 ; i <= n; i++){ a[pos[i]] = i; } for (int i = 1 ; i <= n; i++){ cout << a[i] << " \n" [i == n]; } } signed main () { ios::sync_with_stdio (false );cin.tie (0 );cout.tie (0 ); int T=1 ; if (multi) cin>>T; while (T--){ solve (); } return 0 ; }
Go to Play Maimai DX 题目大意 给定一个只有1,2,3,4的指针,求最短区间满足这个区间内包含1,2,3,4四个数且4这个数的数量至少为k。
题目保证该区间至少有一个1,2,3且至少有k个4
解题思路 双指针,两个指针分别表示此时区间的左右端点,右端点从右边不断加数,左端点判断去掉左端数去掉是否仍满足题意,每次直到左端点右移后不符合含k个4的好区间为止。
时间复杂度:
参考代码 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 #include <bits/stdc++.h> using namespace std;#define endl '\n' #define debug(a) cout<<#a<<"=" <<a<<endl; #define int long long typedef long long LL;typedef pair<int ,int > PII;typedef pair<double ,double > PDD;const double eps=1e-8 ;bool multi=0 ;int n,k;const int N=1e5 +10 ;int a[N];vector<int > cnt (5 ) ;int tot=0 ;int cnt4=0 ;void solve () { cin>>n>>k; for (int i=1 ;i<=n;i++) cin>>a[i]; int ans=1e9 ; int l=1 ; for (int i=1 ;i<=n;i++){ if (!cnt[a[i]]){ tot++; } cnt[a[i]]++; while (1 ){ if (a[l]!=4 &&cnt[a[l]]>1 ||a[l]==4 &&cnt[a[l]]>k){ cnt[a[l]]--; l++; }else { break ; } } if (tot==4 &&cnt[4 ]>=k){ ans=min (ans,i-l+1 ); } } cout<<ans<<endl; } signed main () { ios::sync_with_stdio (false );cin.tie (0 );cout.tie (0 ); int TTT=1 ; if (multi) cin>>TTT; while (TTT--){ solve (); } return 0 ; }
Nazrin the Greeeeeedy Mouse 题目大意 给定n个奶酪按顺序从左往右放成一条线,每个奶酪有它的重量和体积,有m个袋子,每次Nazrin按顺序拿一个袋子装从左往右装奶酪,如果这个奶酪装不了要么破坏这个要么回去拿下一个袋子,如果奶酪被破坏则不能再拿这个奶酪。
解题思路 观察到,那么我们只需要保留最后min{200,n}个即可。
这里我们规定i=1为用最后min{200,n}中的第一个背包
定义为用前第i个背包,拿到第j个奶酪,且第i个背包已经装的体积为k时的获得的最大重量。
它可以从用前i-1个背包,拿到j-1时的最大重量转移过来,如下:
1 2 3 4 5 6 for (int k=0 ;k<=sz[i-1 ];k++){ mx=max (mx,dp[i-1 ][j-1 ][k]); } if (k>=v[j]){ dp[i][j][k]=max ({dp[i][j][k],max (mx,dp[i][j-1 ][k-v[j]])+w[j]}); }
最后的答案是每一轮中拿到最后一个且装的体积最大时的最大值。
时间复杂度: i\le r$$
参考代码 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 #include <bits/stdc++.h> using namespace std;#define endl '\n' #define debug(a) cout<<#a<<"=" <<a<<endl; #define int long long typedef long long LL;typedef pair<int ,int > PII;typedef pair<double ,double > PDD;const double eps=1e-8 ;bool multi=0 ;int n,m;const int N=210 ,M=1e5 +10 ;int v[N],w[N],sz[M];int dp[N][N][N];void solve () { cin>>n>>m; for (int i=n;i>=1 ;i--){ cin>>v[i]>>w[i]; } for (int i=m;i>=1 ;i--){ cin>>sz[i]; } int r=min (m,200LL ); int ans=0 ; for (int i=1 ;i<=r;i++){ for (int j=1 ;j<=n;j++){ int mx=0 ; for (int k=0 ;k<=sz[i-1 ];k++){ mx=max (mx,dp[i-1 ][j-1 ][k]); } for (int k=0 ;k<=sz[i];k++){ dp[i][j][k]=max (mx,dp[i][j-1 ][k]); if (k>=v[j]){ dp[i][j][k]=max ({dp[i][j][k],max (mx,dp[i][j-1 ][k-v[j]])+w[j]}); } } } } for (int i=1 ;i<=r;i++){ ans=max (ans,dp[i][n][sz[i]]); } cout<<ans<<endl; } signed main () { ios::sync_with_stdio (false );cin.tie (0 );cout.tie (0 ); int TTT=1 ; if (multi) cin>>TTT; while (TTT--){ solve (); } return 0 ; }