zoukankan      html  css  js  c++  java
  • NOI Online 提高

    序列

    题意

    有长度为(n)(a,b)两个序列,有m种操作,操作有两种类型,(1 x y)代表这个操作可以让(a[x])(a[y])同时加(1)或者减(1),(2 x y)则代表一个加(1)另一个减(1)
    每种操作可以无限次进行,问能否让(a)(b)完全相等。

    题解

    先让 (a[i]=a[i]-b[i]) ,现在的目的就是让 (a[i]=0)
    首先分两种部分分情况:

        1.只有两个点
        2.操作只有2
    

    只有两个点的情况很容易处理,讨论一下就可以了。
    操作只有2的情况也很容易处理,把操作看成边,发现2边连接的连通块之和为0的话,这个连通块就可以通过不断的2操作和b完全一样。

    整体做法就是结合上面两种情况,我们先把2边连通块缩成一个点,这时候整个图就剩1边了,发现性质,a-b-c这样一个结构,可以看做是a和c连了个2边。

    让新的图建立起新的2边,然后再缩点,这个过程可以通过黑白染色实现。这样缩下去,每个连通块的点数不超过(2),依次判断就行。

    复杂度是(O(n))

    代码

    考场没写出来,只写了暴力。

    现在发现正解比暴力代码好写:

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=2e5+5;
    int n,m;
    vector<int> q2[MAXN],q1[MAXN],q[MAXN];
    int tot;
    int idx[MAXN],vis[MAXN];
    long long a[MAXN],B[MAXN],v[MAXN];
    
    void dfs_init(int x)
    {
        //cout<<" dfs_init "<<x<<" "<<tot<<endl;
        idx[x]=tot;
        v[tot]+=a[x];
        for(int i=0; i<q2[x].size(); i++)
        {
            int nx=q2[x][i];
            if(idx[nx])
                continue;
            dfs_init(nx);
        }
    }
    bool ok=0;
    stack<int> b,w;
    void dfs(int x)
    {
        if(vis[x]==1)
            b.push(x);
        else
            w.push(x);
        for(int i=0; i<q[x].size(); i++)
        {
            int nx=q[x][i];
            if(vis[nx]==vis[x])
                ok=1;
            if(vis[nx])
                continue;
            vis[nx]=3-vis[x];
            dfs(nx);
        }
    }
    
    int main()
    {
        int T;scanf("%d",&T);
    
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1; i<=n; i++)
                scanf("%lld",&a[i]);
            for(int i=1; i<=n; i++)
                scanf("%lld",&B[i]),a[i]-=B[i];
            for(int i=1; i<=m; i++)
            {
                int t,x,y;
                scanf("%d%d%d",&t,&x,&y);
                if(t==1)
                {
                    q1[x].push_back(y);
                    q1[y].push_back(x);
                }
                else
                {
                    q2[x].push_back(y);
                    q2[y].push_back(x);
                }
            }
            for(int i=1; i<=n; i++)
            {
                if(idx[i])
                    continue;
                tot++;
                dfs_init(i);
            }
            for(int i=1; i<=n; i++)
            {
                for(int j=0; j<q1[i].size(); j++)
                {
                    int nx=q1[i][j];
                    q[idx[i]].push_back(idx[nx]);
                    q[idx[nx]].push_back(idx[i]);
                }
            }
            for(int i=1;i<=tot;i++){
                for(int j=0;j<q[i].size();j++){
                    int nx=q[i][j];
                }
            }
            bool ans=1;
            for(int i=1; i<=tot; i++)
            {
                ok=0;
                if(vis[i])
                    continue;
                long long val=0;
                vis[i]=1;
                dfs(i);
                if(ok){
                    while(!b.empty()){
                        val+=v[b.top()];
                        b.pop();
                    }
                    while(!w.empty()){
                        val+=v[w.top()];
                        w.pop();
                    }
                    if(val%2)
                        ans=0;
                }
                else{
                    if(w.empty()){
                        if(v[b.top()]!=0)
                            ans=0;
                        b.pop();
                    }
                    else{
                        while(!b.empty()){
                            val+=v[b.top()];
                            b.pop();
                        }
                        while(!w.empty()){
                            val-=v[w.top()];
                            w.pop();
                        }
                        if(val!=0)
                            ans=0;
                    }
                }
            }
            if(ans)
                printf("YES
    ");
            else
                printf("NO
    ");
            for(int i=1;i<=tot;i++)
            {
                q[i].clear();
                v[tot]=0;
            }
            for(int i=1;i<=n;i++)
            {
                vis[i]=idx[i]=v[i]=0;
                q1[i].clear();
                q2[i].clear();
            }
            tot=0;
            ok=0;
    
        }
    
        return 0;
    }
    

    冒泡排序

    题意

    (n)长度序列(p)(m)个操作,操作分两种,(1 x)表示当前序列(x)位和(x+1)位交换,(2 k)表示询问如果当前序列冒泡(k)轮后的序列的逆序对和。

    题解

    我们逆序对计算方式是算前面比当前数大的个数。那么会发现,一个序列冒泡一次,每个位置的逆序对数量都会(-1),除非那个位置逆序对数为0。

    那么我们用树状数组(线段树也行)去存储逆序对数,树状数组第k个位置存的就是逆序对数为k的位置的逆序对数。查询时查询大于(k)的和,然后每个大于(k)的位置都减去(k)
    即。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=200005;
    int n,m;
    int p[MAXN];
    long long sum[MAXN],maxn[MAXN],tot[MAXN];
    int k[MAXN];
    void update(int l,long long v,long long *sum){
        while(l){
            sum[l]+=v;
            l-=(l&(-l));
        }
    }
    long long get(int l,long long *sum){
        long long an=0;
        while(l<=n){
            an+=sum[l];
            l+=(l&(-l));
        }
        return an;
    }
    
    int main()
    {
    	freopen("bubble.in","r",stdin);
    	freopen("bubble.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&p[i]);
            update(p[i],1,maxn);
            k[i]=get(p[i]+1,maxn);
            update(k[i],k[i],sum);
            update(k[i],1,tot);
        }
    
        for(int i=1;i<=m;i++){
            int t,x;
            scanf("%d%d",&t,&x);
            if(t==1){
                if(p[x]>p[x+1])
                {
                    update(k[x+1],-k[x+1],sum);
                    update(k[x+1],-1,tot);
                    k[x+1]--;
                    update(k[x+1],k[x+1],sum);
                    update(k[x+1],1,tot);
                }
                else{
                    update(k[x],-k[x],sum);
                    update(k[x],-1,tot);
                    k[x]++;
                    update(k[x],k[x],sum);
                    update(k[x],1,tot);
                }
                swap(p[x],p[x+1]);
                swap(k[x],k[x+1]);
            }
            else{
                long long ans=get(x+1,sum)-x*get(x+1,tot);
                printf("%lld
    ",ans);
            }
        }
    
        return 0;
    }
    

    最小环

    题意

    给你个(n)的环,环上每个位置都有值,给(m)次询问,对于每次询问给个(k),你需要把环上的值交换顺序,以让环上每个相距为(k)的数相乘的和最大。

    题解

    对于询问(k),相当于是把环分成(gcd(n,k))个小环,发现每个小环都是从大到小分配数的规律,然后因为环的种类只有(sum gcd(i,n))种,预处理即可。复杂度(o(nsqrt{n}))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    int n,m;
    long long a[400005];
    bool vis[400005];
    long long ans[400005];
    int main()
    {
    	freopen("ring.in","r",stdin);
    	freopen("ring.out","w",stdout);
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]),ans[0]+=a[i]*a[i];
        sort(a+1,a+1+n);
    
        for(int i=1;i*2<=n;i++){
            int g=__gcd(i,n);
            if(vis[g])continue;
            vis[g]=1;
            int l=n/g;
            int p=n-l+1;
            ans[g]+=a[n]*a[n-1];
            for(int j=n;j>=1;j--){
                if(j==p){
                    if(j>1)ans[g]+=a[j-1]*a[j-2];
                    p=j-l;continue;
                }
                ans[g]=ans[g]+(a[j]*a[max(j-2,p)]);
            }
        }
    
        for(int i=1;i<=m;i++){
            int k;scanf("%d",&k);
            if(k==0)printf("%lld
    ",ans[0]);
            else{
            	k=__gcd(k,n);
            	printf("%lld
    ",ans[k]);
        	}
        }
        return 0;
    }
    
  • 相关阅读:
    关于java.lang.OutOfMemoryError: Java heap space的错误分析
    对TCP/IP网络协议的深入浅出归纳
    leetcode面试准备:Contains Duplicate I && II
    leetcode面试准备:Count Complete Tree Nodes
    leetcode面试准备: Jump Game II
    leetcode面试准备: Jump Game
    LeetCode解题报告:Linked List Cycle && Linked List Cycle II
    最小栈的实现与优化
    面试:归并排序
    leetcode面试准备:Decode Ways
  • 原文地址:https://www.cnblogs.com/redegg/p/12433639.html
Copyright © 2011-2022 走看看