noip模拟【20190811】

昨晚没休息好,今天不是犯困就是想一些奇奇怪怪的事情。
定个小目标,在校期间就不要十一点半以后睡觉了。


T1

[树状数组]

每次交换相邻的两张卡片,将序列排成先增后减的序列,问最小交换次数。
枚举每个数作为中间数。不中间数一定是最大的那个。
如果要将一个序列排成递增序列,那么只需求出逆序数即可。
如果要将一个序列排成递减序列,类似地求出正序数即可。
那如果要排成增减序列。那不也是求逆序对嘛。
类似地,对于每一个数我们求出它左边比它大的数的个数和右边比它大的数的个数取min。
用树状数组维护即可。

具体例子:
原序列: 4 8 2 7 5 6 1 3
逆序对有:
(4,2), (4,1), (4, 3), 
(8,2), (8,7), (8,5), (8,6), (8,1), (8,3), 
(2,1), 
(7,5), (7,6), (7,1), (7,3), 
(5,1), (5,3), 
(6,1), (6,3), 
逆序数为18
逆序列为:6 2 5 0 2 2 1 0

交换过程如下:
 4 8 2 7 5 6 1 3
(1向左交换6次)
 4 8 2 7 5 1 6 3
 4 8 2 7 1 5 6 3
 4 8 2 1 7 5 6 3
 4 8 1 2 7 5 6 3
 4 1 8 2 7 5 6 3
 1 4 8 2 7 5 6 3
(2向左交换2次)
 1 4 2 8 7 5 6 3
 1 2 4 8 7 5 6 3
(3向左交换5次)
 1 2 4 8 7 5 3 6
 1 2 4 8 7 3 5 6
 1 2 4 8 3 7 5 6
 1 2 4 3 8 7 5 6
 1 2 3 4 8 7 5 6
(5向左交换2次)
 1 2 3 4 8 5 7 6
 1 2 3 4 5 8 7 6
(6向左交换2次)
 1 2 3 4 5 8 6 7
 1 2 3 4 5 6 8 7
(7向左交换1次)
 1 2 3 4 5 6 7 8
思考:
    上面的过程,其实就是冒泡排序的过程,每一轮都能将最小的数冒到最左边。所区别的是,冒泡排序是直接两两比较、进行交换,而这里是先找逆序列,然后不比较、直接交换。两者在程序代码上的复杂度是差不多的。这里提供的一点是最少的交换次数=序列的逆序数。

【code】

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define File "updown"
inline void file(){
    freopen(File".in","r",stdin);
    freopen(File".out","w",stdout);
}
inline int read(){
    int x = 0,f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch=='-')f = -1; ch = getchar();} 
    while(ch >= '0' && ch <= '9'){x = (x<<1) + (x<<3) + ch-'0'; ch = getchar();}
    return x*f;
} 
const int mxn = 1e5 + 5;
int n;

inline int min_(int x,int y){
    return x > y ? y : x;
}
ll c[mxn];
inline ll lowbit(ll x){
    return x&(-x);
}
inline void update(int x,int d){
    while(x <= n){
        c[x] += d;    
        x += lowbit(x);
    }
}
inline ll query(int x){
    ll ans = 0;
    while(x){
        ans += c[x];
        x -= lowbit(x);
    }        
    return ans;
} 

vector<int> a[mxn];
#define pb push_back

int main(){
    file();
    n = read();
    for(int i = 1;i <= n; ++i){
        int x = read();
        a[x].pb(i);
        update(i,1);
    }
    ll ret = n,ans = 0;
    for(int i = 1;i <= mxn; ++i){
        if(a[i].empty()) continue;
        for(int j = 0;j < a[i].size(); ++j){
            update(a[i][j],-1);
            ret--;
        }
        for(int j = 0;j < a[i].size(); ++j)
            ans += min_(ret-query(a[i][j]),query(a[i][j]-1));
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

 

 

T2

[贪心]

每个人从座位走到出口,如果不考虑可能会被前面的人挡住的情况,所需要的时间是固定的。
大胆猜想,两个人可能会发生冲突,当且仅当所需要最短时间相同。
我们现在得到了一串两两可能相同的序列。
将最短时间进行排序,所需时间用 (可能时间+1) 和 t[i]中较大的那个更新。

【code】

#include<bits/stdc++.h>

using namespace std;

int t[501000],r,s,p;

inline int read(){   
    int num = 0; bool f = 1;char ch = getchar();   
    while(ch < '0' || ch > '9') { if(ch == '-') f = 0;ch = getchar();}   
    while(ch >= '0' && ch <= '9') {num = num * 10 + ch - '0';ch = getchar();}   
    return num = f ? num: -num;   
} 

int main(){
    freopen("plan.in","r",stdin);
    freopen("plan.out","w",stdout);
    scanf("%d%d%d",&r,&s,&p);
    for (int i = 1; i <= p; ++i){
        int x,y;
        x = read(); y = read();
        t[i] = r-x+1+(y<=s?s+1-y:y-s);
    }
    int tmp = 0;
    sort(t+1,t+p+1);
    for (int i = 1; i <= p; ++i) tmp = max(t[i],tmp+1);
    printf("%d\n",tmp);
    return 0;
}
View Code

 

 

 

T3

[树链剖分,虚树]

可以写暴力。
怎样把(u,v)路径上的点提取出来放入一个集合中。

Dfs时记录当前点的上一个节点是什么即可。20分。

60分暴力是树链剖分部分。

100分为虚树。

这个做法我留坑了。。。

(前面还有好多题没订完啊啊啊啊

【code】

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define inf 1<<30    
#define File "coding"
inline void file(){
    freopen(File".in","r",stdin);
    freopen(File".out","w",stdout);
} 
inline int read(){
    int x = 0,f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch=='-')f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x<<1) + (x<<3) + ch-'0'; ch = getchar();}
    return x*f; 
}
const int mxn = 5e5 + 10;
int n,m;
struct edge{
    int y,nxt;
}e[mxn<<1];

int to[mxn],len;
inline void add(int xx,int yy){
    e[++len].nxt = to[xx];
    to[xx] = len;
    e[len].y = yy;
}

int fa[mxn],d[mxn];
void dfs(int x){
    for(int i = to[x]; i;i = e[i].nxt){
        int y = e[i].y;
        if(y == fa[x]) continue;
        fa[y] = x;
        d[y] = d[x]+1;
        dfs(y);
    }
}

ll w[mxn];

///*
inline void wor1(int x,int y,int k){
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        w[x] += (ll)k;
        x = fa[x];
    }
    while(x != y){
        w[x] += (ll)k;
        w[y] += (ll)k;
        x = fa[x];
        y = fa[y];
    }
    w[x] += (ll)k;
}
inline void wor2(int x,int y,int k){
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        w[x] ^= (ll)k;
        x = fa[x];
    } 
    while(x != y){
        w[x] ^= (ll)k;
        w[y] ^= (ll)k;
        x = fa[x];
        y = fa[y];
    }
    w[x] ^= (ll)k;
}
inline void wor3(int x,int y,int k){
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        w[x] = (w[x] >= k) ? (w[x]-k) : w[x];
        x = fa[x];
    } 
    while(x != y){
        w[x] = (w[x] >= k) ? (w[x]-k) : w[x];
        w[y] = (w[y] >= k) ? (w[y]-k) : w[y];
        x = fa[x];
        y = fa[y];
    }
    w[x] = (w[x] >= k) ? (w[x]-k) : w[x];
}
inline void wor4(int x,int y){
    ll ret(0);
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        ret += w[x];
        x = fa[x];
    } 
    while(x != y){
        ret += w[x];
        ret += w[y];
        x = fa[x];
        y = fa[y];
    }
    ret += w[x];
    printf("%lld\n",ret);
}
inline void wor5(int x,int y){
    ll ret(0);
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        ret ^= w[x];
        x = fa[x];
    } 
    while(x != y){
        ret ^= w[x];
        ret ^= w[y];
        x = fa[x];
        y = fa[y];
    }
    ret ^= w[x];
    printf("%lld\n",ret);
}
inline ll max_(ll x,ll y){
    return x > y ? x : y;
}
inline ll min_(ll x,ll y){
    return x > y ? y : x;
}
inline void wor6(int x,int y){
    ll mx(0),mn = inf;
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        mx = max_(mx,w[x]);
        mn = min_(mn,w[x]);
        x = fa[x];
    } 
    while(x != y){
        mx = max_(mx,w[x]);
        mx = max_(mx,w[y]);
        mn = min_(mn,w[x]);
        mn = min_(mn,w[y]);
        x = fa[x];
        y = fa[y];
    }
    mx = max_(mx,w[x]);
    mn = min_(mn,w[x]);
    printf("%lld\n",mx-mn);
}
inline void wor7(int x,int y,int k){
    ll mn = inf;
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        mn = min_(mn,abs(w[x]-k));
        x = fa[x];
    } 
    while(x != y){
        mn = min_(mn,abs(w[x]-k));
        mn = min_(mn,abs(w[y]-k));
        x = fa[x];
        y = fa[y];
    }
    mn = min_(mn,abs(w[x]-k));
    printf("%lld\n",mn);
}
//*/
/*
int t[mxn],tot(0);
inline void test_wor(int x,int y){
    memset(t,0,sizeof t);
    tot = 0;
    if(d[x] < d[y]) swap(x,y);
    while(d[x] != d[y]){
        t[++tot] = x;
        x = fa[x];
    } 
    while(x != y){
        t[++tot] = x;
        t[++tot] = y;
        x = fa[x];
        y = fa[y];
    }
    t[++tot] = x;
    puts("");
    for(int i = 1;i <= tot; ++i) 
        printf("%d ",t[i]);
    puts("");
}
*/
int main(){
//    file();
    n = read(),m = read();
    for(int i = 1;i < n; ++i){
        int x = read(),y = read();
        add(x,y),add(y,x);
    }
    dfs(1);
    /*
    for(int i = 1;i <= m; ++i){
        int x = read(),y = read();
        test_wor(x,y);
    } 
    /*
    6 2
    1 2 
    1 3
    2 4
    2 5
    3 6
    4 5
    4 6
    */
//    /*
    for(int i = 1;i <= m; ++i){
        int opt = read(),x = read(),y = read();
        if(opt==1){
            int k = read(); wor1(x,y,k);
        }else if(opt==2){
            int k = read(); wor2(x,y,k);
        }else if(opt==3){
            int k = read(); wor3(x,y,k);
        }else if(opt==4){
            wor4(x,y);
        }else if(opt==5){
            wor5(x,y);
        }else if(opt==6){
            wor6(x,y);
        }else if(opt==7){
            int k = read(); wor7(x,y,k);
        }
    }
//    */
    return 0;
}
View Code

 

posted @ 2019-08-18 22:14  ve-2021  阅读(179)  评论(0编辑  收藏  举报