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;
    }
    
  • 相关阅读:
    Saltstack module acl 详解
    Saltstack python client
    Saltstack简单使用
    P5488 差分与前缀和 NTT Lucas定理 多项式
    CF613D Kingdom and its Cities 虚树 树形dp 贪心
    7.1 NOI模拟赛 凸包套凸包 floyd 计算几何
    luogu P5633 最小度限制生成树 wqs二分
    7.1 NOI模拟赛 dp floyd
    springboot和springcloud
    springboot集成mybatis
  • 原文地址:https://www.cnblogs.com/redegg/p/12433639.html
Copyright © 2011-2022 走看看