ZOJ Monthly, January 2018 總結。
阿新 • • 發佈:2019-02-12
刷了一下也沒有時間補題,看了別人的題解,就總結一下:
A:水題。
B:
給定n字串,每次詢問查詢兩個字串的一個公共字尾,使得這個公共字尾在n個字串中至少有一個字串S,使得這個公共字尾是S的字首。問滿足條件的最長公共字尾 F,這個字尾F是一個字串的字首的話就++num。輸出num.
題解:
對所有串建立AC自動機,那麼若字首i是字首jj的字尾,說明ii是Fail樹上j的祖先。
所以對於詢問(x,y),答案就是兩點在Fail樹上的LCA在原Trie中子樹內的字串總數。
時間複雜度O(nlogn)。
還有暴力點的題解:求出最長公共字尾,然後列舉字尾 字典樹求ans.
總結:遇到這種字首與字尾相同就應該想到fail指標。
D:
題意:給m個位置,10種人,1,2,3,4,,10第i種人只做 編號為i的倍數的位置,給出每種人的數量,求能安排的最多人數。
lcm(1,2,...,10)=2520,對於模lcm的每個餘數k分析其是否是1到10的倍數,可以發現一共48種本質不同的情況。
那麼計算出每種情況的容量之後,就轉化成了左邊10個點,右邊48個點的最大流。
總結:可以理解為匹配問題,用lcm(週期)來減少點,然後有一些點可以看成相同的,再次減少點,對於相同型別的點可以合併然後加cap就能減少點,減少複雜度。
J:
給定兩個數列a,b,分別在兩個數列上選取相同長度的連續的子序列,使得兩個子序列中Σ(abs(a[i]-b[j])^p)<=v,問有多少種選取方案
暴力:把兩個數列想象成二維陣列:每個對角線就是 對應 連續的子序列,然後對對角線掃一遍。
#include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> using namespace std; typedef long long int ll; const int maxn = 1000; int n,p,a[maxn+5],b[maxn+5]; ll g[maxn*maxn+5],V,w[maxn*maxn+5],ans; ll pows(ll x,int p) { ll r = 1; for(int i=1; i<=p; i++) r*=x; return r; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d %lld %d",&n,&V,&p); for(int i=1; i<=n; i++) scanf("%d",&a[i]); for(int i=1; i<=n; i++) scanf("%d",&b[i]); ans = 0; for(int i=1; i<=n; i++) { int x = i, y = 1, len = 0, l = 1,j=1; ll sum = 0; while(x<=n&&y<=n) { w[++len] = pows(abs(a[x]-b[y]),p); sum+=w[j]; while(sum>V&&l<=j) sum-=w[l++]; ans+=j-l+1; x++,y++,j++; } } for(int i=2; i<=n; i++) { int x = 1, y = i, len = 0, l = 1,j=1; ll sum = 0; while(x<=n&&y<=n) { w[++len] = pows(abs(a[x]-b[y]),p); sum+=w[j]; while(sum>V&&l<=j) sum-=w[l++]; ans+=j-l+1; x++,y++,j++; } } printf("%lld\n",ans); } return 0; }