7月集訓總結 2018.7.26

今天是7月集訓最後一天。7月總共12天的集訓告一段落。
總共參加了11場比賽,有10場沒有發揮好。有些場特別差,只過了一兩題。總成績很差,19名,只能進入浙大B隊。
每場比賽都有很多sb錯誤,對考場上狀態反思不夠,沒有及時改正。考場上精神不夠集中,老是在一個地方卡死,不敢去換一道題做。導致幾乎每一場都有遺憾。還有經常看錯題。
總之我的狀態和水平都還很差,非常需要進一步提高。繼續努力!在接下來的每一天完成任務,爭取在8月集訓前找回自己的狀態!

7。25題解:
A:求長度爲n的只包含兩次“01”的01串。
看清題,答案=C(n + 1,5),n很大,直接矩陣快速冪
B:求字典序第k小子序列。
看清題:相同的子序列只算一次。
然後就是很簡單的主席樹上二分,按位確定。直接枚舉的複雜度也是對的:因爲只會有log(k)個不同的數。相同的數選最前面的因爲所有都可以構造出來。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define N 2000020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8


typedef long long ll;
const ll INF = 1e12;
inline int read(){
    register int num = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
    return num;
}
int n,T,k,mx;
int rt[maxn],ls[N],rs[N],a[maxn],pos[N],tot;
int ans[maxn],id[maxn],cnt;
ll sum[N],f[maxn],g[maxn];

void clear(){
    rep(i,1,n) g[i] = f[i] = 0 , id[i] = rt[i] = 0;
    rep(i,1,tot) sum[i] = 0 , pos[i] = ls[i] = rs[i] = 0;
    mx = tot = cnt = 0;
}
void init(){
    ll cur = 0;
    repd(i,n,1){
        f[i] = min(INF,cur + 1);
        cur = min(INF,cur + f[i] - f[id[a[i]]]);
        id[a[i]] = i;
    }
//  rep(i,1,n) cout<<f[i]<<" ";
//  cout<<endl;
}
inline void copy(int x,int y){
    ls[x] = ls[y] , rs[x] = rs[y] , sum[x] = sum[y];
}
inline void update(int x){
    sum[x] = min(INF,sum[ls[x]] + sum[rs[x]]);
}   
void insert(int &x,int y,int l,int r,int id,ll d,int p){
    x = ++tot;
    if ( l == r ){ pos[x] = p; sum[x] = d; return; }
    int mid = (l + r) >> 1;
    copy(x,y);
    if ( id <= mid ) insert(ls[x],ls[y],l,mid,id,d,p);
    else insert(rs[x],rs[y],mid + 1,r,id,d,p);
    update(x);
}
int query(int x,int l,int r,int &k){
    if ( l == r ) return pos[x];
    int mid = (l + r) >> 1;
    if ( k <= sum[ls[x]] ) return query(ls[x],l,mid,k);
    k -= sum[ls[x]];
    return query(rs[x],mid + 1,r,k);
}
int main(){
    freopen("input.txt","r",stdin);
//  freopen("1.out","w",stdout);
    scanf("%d",&T);
    while ( T-- ){
        clear();
        scanf("%d %d",&n,&k);
        rep(i,1,n) scanf("%d",&a[i]),mx = max(mx,a[i]);
        init();
        repd(i,n,1){
            insert(rt[i],rt[i + 1],1,mx,a[i],f[i],i);
        }
        int cur = 0;
        while ( 1 ){
            cur = query(rt[cur + 1],1,mx,k);
            k-- , ans[++cnt] = a[cur];
            if ( k <= 0 ) break;
            if ( cur == n ) break;
        }
        printf("%d\n",cnt);
        rep(i,1,cnt) printf("%d ",ans[i]);
        printf("\n");
    }
    return 0;
}

C題:給一張有向圖,求刪除每一個點,是否可以讓強連通分量增加。
支配樹好題。對於每個SCC,選一個點,跑支配樹(將邊反向後再跑一次),結論是如果這個點在任一支配樹中是其他點的支配集,就會使強連通分量增加。最後把選的那個點刪除再跑一次SCC check
明天寫,該複習支配樹了!

D題:給n個點,選兩點構成矩形,問最多包含多少個點(兩點中一個點在另一個點的左上角)
維護左上和右下邊界,從左往右掃描,用線段樹維護左上點的最大值,掃到右下時查詢。因爲右下邊界y座標遞增,用一個set維護所有點,然後彈出y座標比當前查詢點小的。
經典的掃描線!
注意set要用multiset,因爲y有重。還有set的遍歷,用iterator

#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define N 400020
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)
#define repd(i,r,l) for (register int i = r ; i >= l ; i--)

const int inf = 1e9;
struct node{
    int y,l,r;  
    node(){};
    node(int y,int l,int r):y(y),l(l),r(r){};
    bool operator < (node a)const{
        if ( y  == a.y ) return 0;
        return y < a.y;
    }
};
struct point{
    int x,y;
    point(){};
    point(int x,int y):x(x),y(y){};
}up[maxn],down[maxn];
multiset <node> s; //要用可重集。並且<只定義了y就是隻與y有關的set
multiset <node>::iterator it1,it2,last;
int mx[N],ls[N],rs[N],tot,add[N],root;
int n,T,m,ans,cnt;
int y[maxn];

inline bool cmpy(point p,point y){ return p.y < y.y; }
inline bool cmpx(point p,point x){ return p.x < x.x; }
void clear(){
    rep(i,1,tot) add[i] = mx[i] = ls[i] = rs[i] = 0;
    tot = m = cnt = root = ans = 0;
    s.clear();
}
inline void ADD(int &x,int d){
    if ( !x ) x = ++tot;
    mx[x] += d , add[x] += d;
}
inline void pushdown(int x){
    if ( add[x] != 0 ){
        ADD(ls[x],add[x]);
        ADD(rs[x],add[x]);
        add[x] = 0;
    }
}
inline void update(int x){
    mx[x] = max(mx[ls[x]],mx[rs[x]]);
}
void modify(int &x,int l,int r,int L,int R,int d){
    if ( L > R ) return;
    if ( !x ) x = ++tot;
    if ( L <= l && R >= r ){ ADD(x,d); return; }
    pushdown(x);
    int mid = (l + r) >> 1;
    if ( L <= mid ) modify(ls[x],l,mid,L,R,d);
    if ( R > mid ) modify(rs[x],mid + 1,r,L,R,d);
    update(x);
}
int query(int x,int l,int r,int L,int R){
    if ( L > R ) return 0;
    if ( !x ) return 0;
    if ( L <= l && R >= r ) return mx[x];
    pushdown(x);
    int mid = (l + r) >> 1,res = 0;
    if ( L <= mid ) res = query(ls[x],l,mid,L,R);
    if ( R > mid ) res = max(res,query(rs[x],mid + 1,r,L,R));
    return res;
}
void build(int &x,int l,int r){
    x = ++tot;
    if ( l == r ) return;
    int mid = (l + r) >> 1;
    build(ls[x],l,mid) , build(rs[x],mid + 1,r);
}
void dfs(int x,int l,int r){
    cout<<x<<" "<<mx[x]<<endl;
    if ( l == r ) return;
    pushdown(x);
    int mid = (l + r) >> 1;
    dfs(ls[x],l,mid) , dfs(rs[x],mid + 1,r);
}
void solve(){
    int cur = 0;
    build(root,1,m);
    rep(i,1,n){
        int l = lower_bound(up + 1,up + m + 1,point(i,y[i]),cmpy) - up,r = upper_bound(up + 1,up + m + 1,point(i,y[i]),cmpx) - up - 1;
//      cout<<l<<" "<<r<<endl;
        node dt = (node){y[i],l,r};
        s.insert(dt);
        modify(root,1,m,l,r,1);
        if ( i == down[cur + 1].x ){
            ++cur;
            it2 = s.lower_bound(dt);
    //      dfs(root,1,m);
    //      cout<<endl;
            for (it1 = s.begin() ; it1 != it2 ; ++it1){ 
                modify(root,1,m,(*it1).l,(*it1).r,-1);
            //  cout<<(*it1).l<<" "<<(*it1).r<<" "<<(*it1).y<<endl;
            }
    //      dfs(root,1,m);
        //  cout<<endl;
            if ( it2 != s.begin() ) s.erase(s.begin(),it2); //set的刪除左閉右開
            ans = max(ans,query(root,1,m,l,r));
            //last = it2;
        }
    }
    printf("%d\n",ans);
}
int main(){
    freopen("input.txt","r",stdin);
    scanf("%d",&T);
    while ( T-- ){
        clear();
        scanf("%d",&n);
        rep(i,1,n) scanf("%d",&y[i]);
        rep(i,1,n) if ( up[m].y < y[i] || !m ) up[++m] = point(i,y[i]);
        repd(i,n,1) if ( down[cnt].y > y[i] || !cnt ) down[++cnt] = point(i,y[i]);
        reverse(down + 1,down + cnt + 1);
        solve();
    }
    return 0;
}

E題:這裏寫圖片描述
n,k<=1e5
sol1:直接枚舉i和i*j,考慮把j劃分爲k個數相乘的方案。
對j的每個質因數分別組合數,插板法C(k+tot - 1,k - 1)。因爲每個質因數之間無序,其他數有序。注意不是直接插板,不一定相鄰質因數組合。
簡單的組合數考場上沒有退出來,去打表浪費了很多時間,應該靜心推一下!
sol2:看成狄利克雷卷積,(f *g)(g(i) = 1)
ans = (f*g^k)因爲卷積的結合律。
g數組快速冪時枚舉倍數就好了
O(nlogn^2)
感覺在這道題上大材小用了,但是這種思路很常見。

//#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8


typedef long long ll;
const ll mod = 1e9 + 7;
inline int read(){
    register int num = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
    return num;
}
ll fac[maxn * 2],inv[maxn * 2],d[maxn],g[maxn],h[maxn];
int a[maxn][20],tot[maxn][20];
int prime[maxn],cnt,tag[maxn],num[maxn],vis[maxn];
int n,k,T;

ll power(ll x,ll y){
    ll res = 1;
    while ( y ){
        if ( y & 1 ) res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
void init(){
    for (int i = 2 ; i <= 100000 ; i++){
        if ( !tag[i] ) prime[++cnt] = i;
        for (int j = 1 ; j <= cnt && prime[j] * i <= 100000 ; j++){
            tag[i * prime[j]] = 1;
            if ( (i % prime[j]) == 0 ) break;
        }
    }
    fac[0] = inv[0] = 1;
    for (int i = 1 ; i <= 150000 ; i++) fac[i] = fac[i - 1] * i % mod;
    inv[150000] = power(fac[150000],mod - 2);
    for (int i = 150000 - 1 ; i >= 1 ; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}

void pre(){
    for(n = 2 ; n <= 100000 ; n++){
        int x = n ; cnt = 0;
        for (int i = 1 ; prime[i] * prime[i] <= x ; i++){
            if ( (x % prime[i]) == 0 ) a[n][++cnt] = prime[i];
            while ( (x % prime[i]) == 0 ) tot[n][cnt]++ , x /= prime[i];
        }
        if ( x > 1 ) a[n][++cnt] = x, tot[n][cnt] = 1;
        num[n] = cnt;
    }
//  num[1] = 1 , f[1][1] = 1;
}
inline ll C(int n,int m){
    if ( n == m || !m ) return 1;
    if ( n < m ) return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int main(){
    freopen("input.txt","r",stdin);
//  freopen("1.out","w",stdout);
    init();
    pre();
    scanf("%d",&T);
    while ( T-- ){
        scanf("%d %d",&n,&k);
        rep(i,1,n) scanf("%lld",&d[i]);
        rep(i,1,n) g[i] = 0,vis[i] = 0;
        rep(i,1,n){
            ll cur = 0;
            for (register int j = 1 ; i * j <= n ; j++){
                cur = 0;
                if ( j == 1 ) cur = 1;
                else if ( vis[j] ) cur = h[j];
                else{
                    cur = 1;
                    for (register int y = 1 ; y <= num[j] ; y++){ //對每個質因數分別算組合數,因爲相同質因數不存在順序問題,只需考慮它插在哪個位置
                        //用插板法算組合數
                        cur = cur * C(k + tot[j][y] - 1,k - 1) % mod;
                    }
                    h[j] = cur; vis[j] = 1;
                }
                    g[i * j] = (g[i * j] + cur * d[i]) % mod;
            }
        }
    //  rep(i,1,n) cout<<h[i]<<" ";
    //  cout<<endl<<endl;
        rep(i,1,n){
            printf("%lld",g[i]);
            if ( i < n ) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}

F題:求和第k大的子序列。看清題,k<=1e5
排序後直接用堆維護,每次取最小增廣。
要麼直接選後一個數,要麼這個數和後一個一起選。
可以證明這樣所有情況都可以枚舉到。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8


typedef long long ll;
inline int read(){
    register int num = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
    return num;
}
int num[10];
inline void write(int x){
    register int cnt = 0;
    if ( !x ){ printf("0"); return; } //脪祿露擰脪陋脤脴脜脨0攏隆攏隆攏隆
    while ( x ) num[++cnt] = x % 10 , x /= 10;
    while ( cnt ) putchar(num[cnt--] + '0');
}
struct node{
    int d,id;
    bool operator < (node a)const{
        return d > a.d;
    }
};
priority_queue <node> heap;
int n,k,T,a[maxn];

int main(){
    scanf("%d",&T);
    while ( T-- ){
        while ( heap.size() ) heap.pop();
        scanf("%d %d",&n,&k);
        rep(i,1,n) scanf("%d",&a[i]);
        sort(a + 1,a + n + 1);
        heap.push((node){a[1],1});
        node ans;
        rep(i,1,k){
            ans = heap.top(); heap.pop();
            int d = ans.d , id = ans.id;
            if ( id < n ){
                heap.push((node){d + a[id + 1] - a[id],id + 1});
                heap.push((node){d + a[id + 1],id + 1});
            }
        }
        printf("%d\n",ans.d);
    }
    return 0;
}

G:撲克牌,直接討論一下勝負情況,或者暴力用代碼枚舉也行。然後組合數。很簡單,應該過掉的

H題:
這裏寫圖片描述
找性質然後暴搜,好題!首先ai>=ci>=bi , ai,ci降序,bi升序,然後對ai和cj是否可以交換判定,利用最優性剪枝顯著優化狀態。
從1e9優化到幾十。
注意不要把剪枝寫錯了,不然會T飛

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
#define maxn 120
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e8


typedef long long ll;
inline int read(){
    register int num = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num = num * 10 + ch - '0' , ch = getchar();
    return num;
}
ll ans;
int a[maxn],b[maxn],c[maxn];
int dt[maxn * 3];
int n,T,cnt;

inline bool cmp(int x,int y){ return x > y; }
inline ll cal(){
    ll cur = 0;
    rep(i,1,n) cur += (a[i] - b[i]) * c[i];
    return cur;
}
void dfs(int x,int A,int C,ll res){
    if ( x == n * 3 + 1 ){
        ans = max(ans,res);
    //  if ( cnt % 100000 == 0 ) cout<<cnt<<endl;
        return;
    }
    if ( A < n ) a[A + 1] = dt[x],dfs(x + 1,A + 1,C,res);
    if ( C < A ){
        c[C + 1] = dt[x];
        rep(i,1,C){ //降序填,越來越小,注意剪枝時的順序!
            if ( ((c[i] > a[C + 1]) && (a[i] < b[i] + c[C + 1])) || (c[i] < a[C + 1] && a[i] > b[i] + c[C + 1]) ) return;
        }
        dfs(x + 1,A,C + 1,res + (ll)(a[C + 1] - b[C + 1]) * c[C + 1]);
    }
}
int main(){
    freopen("input.txt","r",stdin);
    scanf("%d",&T);
    while ( T-- ){
        ans = 0;
        scanf("%d",&n);
        rep(i,1,n * 3) scanf("%d",&dt[i]);
        sort(dt + 1,dt + n * 3 + 1);
        rep(i,1,n) b[i] = dt[i];
    //  sort(dt + n + 1,dt + n * 3 + 1,cmp);
        reverse(dt + n + 1,dt + n * 3 + 1);
        dfs(n + 1,0,0,0);
        printf("%lld\n",ans);
    }
    return 0;
}

7。26題解

A;這裏寫圖片描述
高中幾何題,非常簡單,討論清楚即可。
第一列算式的時候要快,直接設成字母理清思路該怎麼代值,把計算留給電腦
第二想清楚切線+一段圓弧最小

B:求n*n = (k +1) *k/2的第m個解,m<=1e12
貝爾方程,構造解
可以打表找規律,還可以差數列書
明天學習貝爾方程

C:簡單樹狀數組優化dp。注意排序時or相同時,應該把ir小的放在後面,這樣接下來的轉移更優

D:二分圖中霍爾定理的簡單應用,不難。要是看了題應該很快能想出來,可惜沒時間看題,所有時間分配不好!!
明天寫!

E:這裏寫圖片描述
集合n維前綴和裸題。靜下心來看清題意就是一眼題。但是考場上看起來複雜就沒有看,很不應該!
明天寫!

F:區間&,| x,求區間最大值。
對每一位考慮,如果區間這一位都相同整體操作,轉化爲區間加。不同則暴力。複雜度有保證,因爲區間操作後不同的數減少。複雜度O(nlog^2)但是不滿,大膽嘗試就可以過了!
明天寫!

寫在最後:
既然7月集訓沒有打好,那接下來就應該更加努力,完成好每一天的計劃。

7、27
上午我要買自行車,所以休息半天
下午14:00-16:00 CFvirtual
16:00-18:00補題,並把div1剩的題做了
晚上18:30-21:30補今天的題
然後跑步,複習,總結。
11點一定要睡覺!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章