2018.7.14日记&总结

今天发挥也很差,失误很多,效率很低。反思,改正!
上午比赛时很快过了前两题,但是后来精力不够集中。
想出F题n^5做法然后想前缀和优化是错的。本可以直接骗数据+打表,但是不熟悉Assert用法,提交尝试非常无效,导致浪费时间,最后也没过。不应该盲目提交,一定要多动脑子,跳出思维定式。不要用空间计算具体数字。用Assert二分出范围就好。大胆尝试,不要觉得有个细节不行就不去尝试。
今天C题感觉贪心有问题,但最后竟然是正解(到现在我还是觉得有问题,明天再去问)但总之数据不强,大胆尝试很重要
注意默认提交为C,要记得改成C++
不要浮躁去做新题和打其他比赛,比如今天打atcoder又留了好多坑,一定要优先把当天的题调完。今天的题没调太不应该了。
无论是写什么题想清楚再写,绝不要想着边写边调真的非常浪费时间。
吃饭和回寝室必须抓紧时间,回寝室立即洗漱,包括发消息总共最多0.5h
应在11点开始写总结,回顾今天的题和状态,早点睡觉

A:树上求加权中位数:链剖+二分,数据结构好题。
中位数与边权无关,只要距离和最小就行,用lct写了一发,不如链剖好写,因为链剖其实就只需要二分一个中位数,其他都很好维护。注意lct时只要遍历splay就要pushdown,比如二分时要pushdown。而且lct不好求lca(因为根固定,要先access再打标记,于是强行写了个倍增lca,好蠢,巨慢!)注意求lca是dth是不能带边权的。

#pragma GCC optimize(3) 
#include<bits/stdc++.h>
using namespace std;
#define maxn 150020
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]

typedef long long ll;
struct node{
    int next,to,w;
}e[maxn * 2];
struct node2{
    ll sum,mul;
};
int head[maxn],cnt;
int w[maxn],jump[20][maxn],fa[maxn];
int n,T,q,dth[maxn];
ll d[maxn];

struct LCT{
    ll sum[maxn],mul[maxn];
    int fa[maxn],ch[maxn][2],rev[maxn];
    int stack_[maxn],tops;

    void print(int x){
        cout<<x<<" "<<ch[x][0]<<" "<<ch[x][1]<<endl;
        if ( ls(x) ) print(ls(x));
        if ( rs(x) ) print(rs(x));
    }
    void clear(){
        for (int i = 1 ; i <= n ; i++) fa[i] = rev[i] = ch[i][0] = ch[i][1] = 0 , sum[i] = mul[i] = 0;
        tops = 0;
    }
    inline void update(int x){
        sum[x] = sum[ls(x)] + sum[rs(x)] + w[x];
        mul[x] = mul[ls(x)] + mul[rs(x)] + (ll)w[x] * d[x];
    }
    inline void reverse(int x){
        if ( !x ) return;
        swap(ls(x),rs(x));
        rev[x] ^= 1;
    }
    inline void pushdown(int x){
        if ( rev[x] ){
            reverse(ls(x));
            reverse(rs(x));
            rev[x] = 0;
        }
    }
    inline bool isroot(int x){
        return (ls(fa[x]) != x) && (rs(fa[x]) != x); 
    }
    inline void rotate(int x){
        int y = fa[x] , t = rs(y) == x , z = ch[x][1 - t];
        fa[x] = fa[y];
        if ( !isroot(y) ) ch[fa[y]][rs(fa[y]) == y] = x;
        fa[y] = x , ch[x][1 - t] = y , ch[y][t] = z;
        if ( z ) fa[z] = y;
        update(y);
    }
    inline void splay(int x){
        tops = 0 , stack_[++tops] = x;
        for (int i = x ; !isroot(i) ; i = fa[i]) stack_[++tops] = fa[i];
        while ( tops ) pushdown(stack_[tops--]);
        while ( !isroot(x) ){
            int y = fa[x] , z = fa[y];
            if ( !isroot(y) && !((ls(y) == x) ^ (ls(z) == y)) ) rotate(y);
            else rotate(x);
            if ( !isroot(x) ) rotate(x);
        }
        update(x);
    }
    void access(int x){
        for (int t = 0 ; x ; t = x,x = fa[x]){
            splay(x) , ch[x][1] = t , update(x);
        }
    }
    inline void makeroot(int x){
        access(x) , splay(x) , reverse(x);
    }
    inline void link(int x,int y){
        makeroot(x) , splay(x) , access(y) , splay(y);
        fa[x] = y , ch[y][1] = x , update(y);
    }
    int query_mid(int x,int y){
        makeroot(x) , access(y);
      //    print(x);
        splay(x);
        //print(x);
        ll cur = (sum[x] + 1) / 2;
        while ( x ){
            pushdown(x); //在splay上二分查询时一定要pushdown。因为有些标记没有在splay时pushdown
            //只要遍历splay就要pushdown,但直接用access和makeroot查值则不需要
            if ( ls(x) && sum[ls(x)] >= cur ){ x = ls(x); continue; }   
            cur -= sum[ls(x)] + w[x];
            if ( cur <= 0 ) break;
            x = rs(x);
        }
        return x;
    }
    node2 query(int x,int y){
        if ( dth[x] < dth[y] ) return (node2){0,0};
        makeroot(x) , access(y) , splay(x);
        return (node2){sum[x],mul[x]};  
    }
    void modify(int x){
        makeroot(x) , access(x) , splay(x);
        sum[x] = w[x] , mul[x] = d[x] * w[x];
    }
}lct;

void clear(){
    lct.clear();
    for (int i = 1 ; i <= cnt ; i++) e[i].next = e[i].to = 0;
    for (int i = 1 ; i <= n ; i++) fa[i] = head[i] = dth[i] = 0,d[i] = 0;
    cnt = 0;
}
inline void adde(int x,int y,int w){
    e[++cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].w = w;
    head[x] = cnt;
}
void dfs(int x){
    for (int i = head[x] ; i ; i = e[i].next){
        if ( e[i].to == fa[x] ) continue;
        dth[e[i].to] = dth[x] + 1;
        d[e[i].to] = d[x] + e[i].w;
        jump[0][e[i].to] = fa[e[i].to] = x;
        dfs(e[i].to);
    }
}
void init(){
    for (int i = 1 ; i <= 18 ; i++) 
        for (int j = 1 ; j <= n ; j++)
            jump[i][j] = jump[i - 1][jump[i - 1][j]];
    for (int i = 1 ; i <= n ; i++) lct.sum[i] = w[i] , lct.mul[i] = d[i] * w[i];
    for (int i = 2 ; i <= n ; i++) lct.link(i,fa[i]);
}
int lca(int x,int y){
    if ( dth[x] < dth[y] ) swap(x,y);
    for (int i = 18 ; i >= 0 ; i--) if ( dth[jump[i][x]] >= dth[y] ) x = jump[i][x];
    if ( x == y ) return x;
    for (int i = 18 ; i >= 0 ; i--) if ( jump[i][x] != jump[i][y] ) x = jump[i][x] , y = jump[i][y];
    return fa[x];
}
int getson(int x,int y){
    for (int i = 18 ; i >= 0 ; i-- ) if ( dth[jump[i][x]] > dth[y] ) x = jump[i][x];
    return x;
}
ll getans(int x,int y){
    int lca_ = lca(x,y) , mid = lct.query_mid(x,y); ll ans = 0;
    if ( lca(x,mid) == mid ){
        node2 cur = lct.query(y,lca_);
        ans += cur.mul + cur.sum * (d[mid] - 2ll * d[lca_]);
        cur = lct.query(fa[mid],getson(x,lca_));
        ans += cur.sum * d[mid] - cur.mul;
        cur = lct.query(x,mid);
        ans += cur.mul - cur.sum * d[mid];
    }
    else{
        node2 cur = lct.query(x,lca_);
        ans += cur.mul + cur.sum * (d[mid] - 2 * d[lca_]);
        cur = lct.query(fa[mid],getson(y,lca_));
        ans += cur.sum * d[mid] - cur.mul;
        cur = lct.query(y,mid);
        ans += cur.mul - cur.sum * d[mid];
    }
    return ans;
}
int main(){
    freopen("input.txt","r",stdin);
    scanf("%d",&T);
    while ( T-- ){
        clear();
        scanf("%d",&n);
        for (int i = 1 ; i <= n ; i++) scanf("%d",&w[i]);
        for (int i = 1 ; i < n ; i++){
            int x,y,w;
            scanf("%d %d %d",&x,&y,&w);
            adde(x,y,w);
            adde(y,x,w);
        }
        dth[1] = 1 , d[1] = 0, dfs(1) , init();
        scanf("%d",&q);
        while ( q-- ){
            int t,x,y;
            scanf("%d %d %d",&t,&x,&y);
            if ( t == 1 ) printf("%lld\n",getans(x,y));
            else w[x] = y , lct.modify(x);
        }
    }
    return 0;
}

B: lucas定理裸题,也可以打表,拆成二进制找规律
C:贪心,感觉题解有点问题QAQ(没有问题是我自己想错了!)
D:有n次操作(n<=100),每次可将a*=b,或b++,求最后a,b在【L,R】的方案数,L,R<= 1e9
利用性质b <= 100,a只有100以内质数组成,a总共只有2e6个,然后可以dp[i]表示拼出i最少的操作数,统计答案即可
统计次数时从小到大枚举b,用当前b再来更新一次

#include<bits/stdc++.h>
using namespace std;
#define maxn 3000020
#define N 120
#define inf 1e9

typedef long long ll;
int f[maxn],a[maxn],num[maxn],tot;
int prime[N],cnt,tag[N];
int T,L,R,p;

void dfs(int x,int cur,int tag){
    if ( cur > 100 && tag ) a[++tot] = cur;// printf("%d %d\n",cur,tot);
    if ( x == cnt + 1 ) return;
    if ( (ll)cur * prime[x] <= inf ) dfs(x,cur * prime[x],1);
    dfs(x + 1,cur,0);
}
void init(){
    for (int i = 2 ; i <= 100 ; i++){
        if ( !tag[i] ) prime[++cnt] = i;
        for (int j = 1 ; j <= cnt && prime[j] * i <= 100 ; j++){
            tag[i * prime[j]] = 1;
            if ( ( i % prime[j]) == 0 ) break;
        }
    }
    for (int i = 1 ; i <= 100 ; i++) a[++tot] = i;
    dfs(1,1,0);
    sort(a + 1,a + tot + 1);
    memset(f,0x3f,sizeof(f)) , memset(num,0x3f,sizeof(num));
    for (int i = 1 ; i <= 100 ; i++) num[i] = i;
    num[1] = 0 , f[0] = f[1] = 0;
    for (int p = 2 ; p < 100 ; p++){
        f[p] = 1; //f数组表示a为i的步数,所以要把b乘给a
        for (register int i = 1 , k = 1 ; i <= tot ; i++){
            if ( (ll)a[i] * p > inf ) break;
            while ( a[k] < (ll)a[i] * p ) k++;
            if ( a[k] == (ll)a[i] * p ) f[k] = min(f[k],f[i] + 1);
            num[k] = min(num[k],f[k] + p);
        }
    }
//  for (int i = 1 ; i <= 100 ; i++) cout<<num[i]<<endl;
}
int main(){
    freopen("input.txt","r",stdin);
    freopen("1.out","w",stdout);
    scanf("%d",&T);
    init();
    while ( T-- ){
        int ans = 0;
        scanf("%d %d %d",&L,&R,&p);
        int l = lower_bound(a + 1,a + tot + 1,L) - a , r = upper_bound(a + 1,a + tot + 1,R) - a - 1;
        for (int i = l ; i <= r ; i++) if ( num[i] <= p ) ans++;
        if ( l == 0 ) ans++;
        printf("%d\n",ans);
    }
    return 0;
}

E:构造图,分类讨论。图相当于拓扑序,即一个点的边集为另一个子集,用斯特林数算组合数,明天写
F:dp计数,好题!有个神奇的优化,分开枚举将n^2转化为n * 2,把位置拆开左右括号分开枚举,但是有括号先枚举防止刚开就闭的无效转移,
2018.9.14理清思路一遍过

#include<bits/stdc++.h>
using namespace std;
#define maxn (1 << 22)
#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;
ll f[220][120][120],fac[220],inv[220];
int n,K,T;

inline 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(){
    fac[0] = inv[0] = 1;
    rep(i,1,100) fac[i] = fac[i - 1] * i % mod , inv[i] = power(fac[i],mod - 2);
}
inline ll C(int n,int m){
    if ( n < m ) return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
inline void up(ll &x,ll y){ x = (x + y) % mod; }
int main(){
    init();
    while ( ~scanf("%d %d %d",&n,&K,&T) ){
        memset(f,0,sizeof(f));
        f[0][0][0] = 1;
        rep(i,0,n * 2 - 1){
            rep(k,0,K){
                rep(j,0,min(T,(int)k)){
                    if ( !f[i][j][k] ) continue;
                    if ( i & 1 ){
                        rep(l,0,min(T - j,K - k))
                            up(f[i + 1][j + l][k + l],f[i][j][k] * C(K - k,l));
                    }
                    else{
                        rep(l,0,j)
                            up(f[i + 1][j - l][k],f[i][j][k] * C(j,l));
                    }
                }
            }
        }
        ll ans = 0;
        rep(i,0,T) up(ans,f[n * 2][i][K]);
        cout<<ans<<endl;
    }
}



G:统计与所有点曼哈顿距离和为C的(u,v)点的个数。固定x,y单调。所以单调扫即可。这么简单的题应该很好想。

所以今天理论上能过4题,实际只过2题。非常不应该。离前5的目标还差很远!!!加油!更加高效的写题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值