2020牛客暑期多校訓練第二場部分
2020牛客多校訓練第二場
出題數 2 --- D題(真水題) 和 F題(滑動視窗)
D、Duration
#include <iostream> #include <cstdio> #include <algorithm> #include <unordered_map> #include <vector> #include <map> #include <list> #include <queue> #include <cstring> #include <cstdlib> #include <ctime> #include <cmath> #include <stack> #include <set> #pragma GCC optimize(3 , "Ofast" , "inline") using namespace std ; #define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0) #define x first #define y second typedef long long ll ; const double esp = 1e-6 , pi = acos(-1) ; typedef pair<int , int> PII ; const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7; ll in() { ll x = 0 , f = 1 ; char ch = getchar() ; while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;} while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ; return x * f ; } int main() { int ah , am , as , bh , bm , bs ; scanf("%d:%d:%d" , &ah , &am , &as) ; scanf("%d:%d:%d" , &bh , &bm , &bs) ; ll a = ah * 3600 + am * 60 + as ; ll b = bh * 3600 + bm * 60 + bs ; cout << abs(a - b) << endl ; return 0 ; } /* */
F Fake Maxpooling
整個矩陣很好求,然後要求求每個k * k矩陣的最大值,我先預處理出來每個k * 1的最大值,然後按照上圖滑動視窗的時候,左面丟出一個k * 1, 右面多出一個k * 1, 直接按照滑動視窗的模式就可以,然後這是一行的, 然後列舉每行, 做滑動視窗就行了
#include <iostream> #include <cstdio> #include <algorithm> #include <unordered_map> #include <vector> #include <map> #include <list> #include <queue> #include <cstring> #include <cstdlib> #include <ctime> #include <cmath> #include <stack> #include <set> #pragma GCC optimize(3 , "Ofast" , "inline") using namespace std ; #define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0) #define x first #define y second typedef long long ll ; const double esp = 1e-6 , pi = acos(-1) ; typedef pair<int , int> PII ; const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7; ll in() { ll x = 0 , f = 1 ; char ch = getchar() ; while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;} while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ; return x * f ; } int a[5010][5010] , ans = 0 , maxn[5010][5010] ; int q[5010] ; int main() { int n = in() , m = in() , k = in() ; for(int i = 0; i < n ;i ++ ) { for(int j = 0; j < m ;j ++ ) { a[i][j] = 1ll * (i + 1) * (j + 1) / __gcd(i+ 1 ,1 + j) ; } } for(int j = 0; j < m ;j ++ ) { int hh = 0 , tt = -1 ; for(int i = 0; i < n ;i ++ ) { if(i - k + 1 > q[hh]) ++ hh ; while(hh <= tt && a[i ][j] >= a[q[tt]][j]) -- tt ; q[++ tt] = i ; if(i + 1 >= k) maxn[i - k + 1][j] = a[q[hh]][j] ; } } ll ans = 0 ; for(int i = 0 ;i < n ;i ++ ) { int hh = 0 , tt = -1 ; for(int j = 0 ;j < m ;j ++ ) { if(j - k + 1 > q[hh]) ++ hh ; while(hh <= tt && maxn[i][j] >= maxn[i][q[tt]]) -- tt ; q[++ tt] = j ; if(j + 1 >= k) { ans += 1ll * maxn[i][q[hh]] ; } } } cout << ans << endl ; return 0 ; } /* */
補題系列
C Cover the Tree
一條鏈 , 兩個端點, 並且這兩個端點都是度為1的點 , 要求最小的數量,就是(n + 1) / 2, 如果n是偶數的話, 就兩兩配對,如果是奇數的話,再多一條鏈。那麼怎麼配對呢。這個看的大佬的程式碼是這樣寫的。
邊(u , v) , u < v, 最終的圖也就是上圖 , 就直接小標號的葉子節點匹配大標號的葉子節點
#include <iostream> #include <cstdio> #include <algorithm> #include <unordered_map> #include <vector> #include <map> #include <list> #include <queue> #include <cstring> #include <cstdlib> #include <ctime> #include <cmath> #include <stack> #include <set> #pragma GCC optimize(3 , "Ofast" , "inline") using namespace std ; #define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0) #define x first #define y second typedef long long ll ; const double esp = 1e-6 , pi = acos(-1) ; typedef pair<int , int> PII ; const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7; ll in() { ll x = 0 , f = 1 ; char ch = getchar() ; while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;} while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ; return x * f ; } int deg[N] , res[N]; int main() { int n = in() ; for(int i = 1 , a ,b ;i < n ;i ++ ) { a = in() , b = in() ; deg[a] ++ , deg[b] ++ ; } int ans = 0 ; for(int i = 1; i <= n ;i ++ ) if(deg[i] == 1) res[++ ans] = i ; cout << (ans + 1) / 2 << endl ; for(int i = 1; i <= (ans + 1) / 2;i ++ ) cout << res[i] << " " << res[ans / 2 + i] << endl ; return 0 ; } /* */
J.Just Shuffle
此題相當於一個單位置換群e,通過置換 P置換群 k次得到一個置換群A
\[e * P^k = A\\ e*P = A^{k^{-1}}\\P = A^{k^{-1}} \]
然後將i和a[i]連線起來,就會得到一些環,每個環就是一個置換群,也就是每個A,然後求A的逆元就行了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
ll x = 0 , f = 1 ;
char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
return x * f ;
}
int vis[N] , a[N] , b[N] , res[N] ;
int main()
{
int n = in() , k = in() ;
for(int i = 1; i <= n ;i ++ ) a[i] = in() ;
for(int i = 1; i <= n ;i ++ ) {
if(vis[i]) continue ;
int pos = i ;
vector<int> ans ;
while(!vis[pos]) ans.push_back(pos) , vis[pos] = 1 , pos = a[pos] ;
int size = ans.size() ;
int t = 0 ;
while(t < size) {
if(1ll * t * k % size == 1) break ;
t ++ ;
}
for(int i = 0 ;i < size ;i ++ )
res[ans[i]] = ans[(i + t) % size] ;
}
for(int i = 1; i <= n ;i ++ ) cout << res[i] << " " ;
puts("") ;
return 0 ;
}
/*
*/
B、Boundary
解法一:算圓心座標
列舉兩點 , 加上原點,總共三點,三點定圓
\[(x - x[i]) ^ 2 + (y - y[i]) ^ 2 = x ^ 2 + y ^ 2 \]
\[(x - x[j]) ^ 2 + (y - y[j]) ^ 2 = x ^ 2 + y ^ 2 \]
\[x = \frac{b[j] * (x[i] ^ 2 + y[i] ^ 2) - b[i] * (x[j] ^ 2 + y[j] ^ 2)}{2x[i] * y[j] - 2 * x[j] * y[i]} \]
\[y = \frac{x[j] * (x[i] ^ 2 + y[i] ^ 2) - x[i] * (x[j] ^ 2 + y[j] ^ 2)}{2x[j] * y[i] - 2 * x[i] * y[j]} \]
先判斷一下分母是否為0 , 記得用long long
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-7 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
ll x = 0 , f = 1 ;
char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
return x * f ;
}
ll x[N] , y[N] ;
double get(int i){
return 1ll * x[i] * x[i] + y[i] * y[i] ;
}
vector<pair<double , double>> ans ;
int main()
{
int n = in() ;
for(int i = 1; i <= n ;i ++ ) x[i] = in() , y[i] = in() ;
int res = 0 ;
for(int i = 1; i <= n ;i ++ ) {
ans.clear() ;
for(int j = 1 ; j <= n ;j ++ ) {
ll yy = x[j] * get(i) - x[i] * get(j) ;
ll t = 2 * x[j] * y[i] - 2 * x[i] * y[j] ;
if(t == 0) continue ;
ll xx = y[j] * get(i) - y[i] * get(j) ;
ans.push_back({(double)xx / (-t) , (double) yy / t}) ;
}
sort(ans.begin() , ans.end()) ;
int cnt = 1 , maxn = 0 ;
for(int i = 1 ; i < ans.size() ;i ++ )
{
if(fabs(ans[i].x - ans[i - 1].x) < esp && fabs(ans[i].y - ans[i - 1].y) < esp) cnt ++;
else cnt = 1 ;
maxn = max(maxn , cnt) ;
}
res = max(res , maxn + 1) ;
}
cout << res << endl ;
return 0 ;
}
/*
*/
解法二:算夾角
這兩個角一定相同,同時為了避免重複,在上圖中二選一,也就是選擇一個判斷標準,op向量*oa向量 < 0, 也可以選擇大於0 , 或者選擇ap * ao, 然後算夾角acos(dot(ap , ao) / (len(ap) * len(ao))) , 最後隨便統計一下, 不過精度好象要調到1e-9, 在1e-8都是不能完全通過的(也可能寫的程式不太對)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef long long ll ;
const double esp = 1e-7, pi = acos(-1) ;
typedef pair<double , double> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
ll in()
{
ll x = 0 , f = 1 ;
char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
return x * f ;
}
PII a[N] ;
double dot(PII a , PII b){
return a.x * b.x + a.y * b.y ;
}
double cross(PII a , PII b){
return a.x * b.y - a.y * b.x ;
}
double c[N] ;
double len(PII a){
return sqrt(a.x * a.x + a.y * a.y) ;
}
double calc(PII a , PII b){
double lena = len(a) , lenb = len(b) ;
return acos(dot(a , b) / (lena * lenb)) ;
}
int main()
{
int n = in() ;
for(int i = 1; i <= n ;i ++ ) cin >> a[i].x >> a[i].y ;
int ans = 0 ;
for(int i = 1; i <= n ;i ++ ) {
int cnt = 0 ;
for(int j = 1; j <= n ;j ++ ) {
PII op = a[i] , oa = a[j] ;
PII ap = {a[j].x - a[i].x , a[j].y - a[i].y} , ao = {-a[j].x ,- a[j].y} ;
if(cross(op , oa) < 0) c[++ cnt] = calc(ap , ao) ;
}
sort(c + 1 , c + cnt + 1) ;
int maxn = 0 , res = 0 ;
double t = 0 ;
for(int i = 1; i <= cnt ;i ++ ) {
if(fabs(c[i] - t) < esp) res ++ ;
else res = 1 , t = c[i] ;
maxn = max(maxn , res) ;
}
ans = max(ans , maxn) ;
}
cout << ans + 1 << endl ;
return 0 ;
}
/*
*/
A.All with Pairs
看到這題的第一個想法就是先預處理出來每個字串的所有後綴,用map存起來,然後再列舉每個字串的字首,判斷有多少個之前map儲存的hash值個數,但是發現題目要求是要求最大,而不是求所有的,上面的這個做法可以將所有符合字首等於字尾的貢獻求出來, 但不能求最大的。然後發現一個小小的性質,kmp演算法next陣列的經典應用
上圖兩個字串匹配中藍色豎槓之間的是字首等於字尾的部分,但是是要求最大的,我們接著往下面匹配
突然後面這個綠色之間的匹配的一部分比藍色部分更大,也就是說當前這個答案更優,那我們就不要前面那個,但是發現
對於黑色曲線,我們發現,1、2、3、4、5號線段字串部分全部都是相同的,也就是1 和 5是相同的,這不就是kmp裡面如果列舉到5部分突然失配了, 會跳到1部分嘛,對於這個性質應用到當前題目上面就是-----題目要求最大,我們就把所有字首字尾相同的部分都直接算答案,不過面對上圖這個情況,我們可以再kmp即將失配的時候,也就是i失配的時候,將next[i]部分的貢獻減掉,即h[ne[j]] -= h[j], 這樣就在把所有貢獻都加上 的同時, 減掉對於當前貢獻來說前面貢獻比較小的部分,就是最大的貢獻 , 即題目所求
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
#define ios ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0)
#define x first
#define y second
typedef unsigned long long ll ;
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 998244353 , base = 131;
ll in()
{
ll x = 0 , f = 1 ;
char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
return x * f ;
}
unordered_map<ll , int> mp ;
void Hash(string s){
ll res = 0 , p = 1 ;
for(int i = s.size() - 1; i >= 0 ;i --) {
res += p * s[i] ;
p *= base ;
mp[res] ++ ;
}
return ;
}
string s[N] ;
int h[N] , ne[N] ;
void get(string s){
for(int i = 2, j = 0 ;i < s.size() ;i ++ ) {
while(j && s[i] != s[j + 1]) j = ne[j] ;
if(s[i] == s[j + 1]) j ++ ;
ne[i] = j ;
}
return ;
}
int main()
{
int n ;
cin >> n ;
for(int i = 1; i <= n ;i ++ ) {
cin >> s[i] ;
Hash(s[i]) ;
}
ll ans = 0 ;
for(int i = 1; i <= n ;i ++ ) {
ll res = 0 ;
for(int j = 0 ;j < s[i].size() ;j ++ ) {
res = res * base + s[i][j] ;
h[j + 1] = mp[res] ;
}
s[i] = " " + s[i] ;
get(s[i]) ;
for(int j = 1 ; j < s[i].size() ;j ++ )
h[ne[j]] -= h[j] ;
for(int j = 1; j < s[i].size() ;j ++ )
ans = (ans + 1ll * h[j] * j % mod * j % mod) % mod ;
}
cout << ans << endl ;
return 0 ;
}
/*
*/
剩下幾題咱也不會