• USACO USOpen 2020 Gold T1 Hair Cut 题解


    Problem

    给出一个数组,求各个 (kin[0,N)) 下数组中所有大于 (k) 的值改为 (k) 后的逆序对个数。

    Solution

    (O(N^2log N)) 解法

    Solution

    暴力

    Code

    #include<cstdio>
    #include<stack>
    #define ls pos<<1
    #define rs (pos<<1)+1
    #define MID (tree[pos].l+tree[pos].r)>>1
    const int MAXN=5e3+5;
    struct Tree{int l,r,val;bool clear;};
    Tree tree[MAXN*4];int n,a[MAXN];
    std::stack<int>sta;
    void BuildTree(int l,int r,int pos)
    {
    	tree[pos].l=l;tree[pos].r=r;tree[pos].clear=0;tree[pos].val=0;
    	if(l==r) return;
    	int mid=MID;
    	BuildTree(l,mid,ls);BuildTree(mid+1,r,rs);
    }
    void DownReload(int pos)
    {
    	if(tree[pos].clear)
    	{
    		tree[ls].val=tree[rs].val=0;
    		tree[ls].clear=tree[rs].clear=1;
    		tree[pos].clear=0;
    	}
    }
    void Update(int target,int pos)
    {
    	if(tree[pos].l==tree[pos].r)
    	{
    		tree[pos].val++;
    		return;
    	}
    	DownReload(pos);	
    	int mid=MID;
    	if(target<=mid) Update(target,ls); else Update(target,rs);
    	tree[pos].val=tree[ls].val+tree[rs].val;
    }
    int Query(int l,int r,int pos)
    {
    	if(l>r) return 0;
    	if(l==tree[pos].l&&r==tree[pos].r)
    	{
    		return tree[pos].val;
    	}
    	DownReload(pos);
    	int mid=MID;
    	if(r<=mid) return Query(l,r,ls); else if(l>mid) return Query(l,r,rs); else {return Query(l,mid,ls)+Query(mid+1,r,rs);}
    }
    int main()
    {
    	freopen("haircut.in","r",stdin);
    	freopen("haircut.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	BuildTree(0,n,1);
    	for(int i=n-1;i>=0;i--)
    	{
    		int ans=0;
    		tree[1].clear=1;
    		for(int j=1;j<=n;j++)
    		{
    			if(a[j]>i) a[j]=i;
    	//		printf("%d %d %d
    ",i,j,Query(a[j]+1,i,1));
    			ans+=Query(a[j]+1,i,1);
    			Update(a[j],1);
    		}
    		//printf("%d
    ",ans);
    		sta.push(ans);
    	}
    	while(!sta.empty()) {printf("%d
    ",sta.top());sta.pop();}
    	return 0;
    }
    

    (O(N^2)) 解法

    Solution

    考虑到相邻 (k) 之间变化量等于所有 (k+1) 变到 (k) 导致逆序对的减量之和,因此算这个然后逆着推。

    Code

    #include<cstdio>
    #include<stack>
    const int MAXN=1e5+5;
    int n,a[MAXN],table[MAXN],sum[MAXN];
    std::stack<int>sta;
    int main()
    {
    	freopen("haircut.in","r",stdin);
    	freopen("haircut.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	int rp=0;
    	for(int i=n;i>=1;i--)
    	{
    		for(int j=0;j<a[i];j++)
    		{
    			rp+=table[j];
    			sum[j]+=table[j];
    		}
    		table[a[i]]++;
    	}
    	for(int i=n-1;i>=0;i--)
    	{
    //		printf("[%d] ",sum[i]);
    		rp-=sum[i];
    //		printf("%d
    ",rp);
    		sta.push(rp);
    	}
    	while(!sta.empty()) {printf("%d
    ",sta.top());sta.pop();}
    	return 0;
    }
    

    (O(Nlog N)) 做法(AC 做法)

    Solution

    由于 table[j] 每个 (i) 的循环只更新一次,(O(N^2)) 做法中 sum[j]+=table[j]; 明显可以被优化,优化后使得每次 table[j] 变化时用乘法加,想起小学老师的“乘法是加法的整合”(也可能记错了),然后加的次数也能用类似逆序对的方法求出来。另外逆序对可以 (O(Nlog N)) 权值线段树求。

    • 注意开 long long
    • 代码中 lastchange[] 没有用

    Code

    #include<cstdio>
    #include<stack>
    const int MAXN=1e5+5;
    #define ls pos<<1
    #define rs (pos<<1)+1
    #define MID (tree[pos].l+tree[pos].r)>>1
    int n,a[MAXN];long long table[MAXN],sum[MAXN],lastchange[MAXN],lastchangev[MAXN];
    struct Tree{int l,r;long long val;bool clear;};
    Tree tree[MAXN*4];
    std::stack<long long>sta;
    void BuildTree(int l,int r,int pos)
    {
    	tree[pos].l=l;tree[pos].r=r;tree[pos].clear=0;tree[pos].val=0;
    	if(l==r) return;
    	int mid=MID;
    	BuildTree(l,mid,ls);BuildTree(mid+1,r,rs);
    }
    void DownReload(int pos)
    {
    	if(tree[pos].clear)
    	{
    		tree[ls].val=tree[rs].val=0;
    		tree[ls].clear=tree[rs].clear=1;
    		tree[pos].clear=0;
    	}
    }
    void Update(int target,int pos)
    {
    	if(tree[pos].l==tree[pos].r)
    	{
    		tree[pos].val++;
    		return;
    	}
    	DownReload(pos);	
    	int mid=MID;
    	if(target<=mid) Update(target,ls); else Update(target,rs);
    	tree[pos].val=tree[ls].val+tree[rs].val;
    }
    long long Query(int l,int r,int pos)
    {
    	if(l>r) return 0;
    	if(l==tree[pos].l&&r==tree[pos].r)
    	{
    		return tree[pos].val;
    	}
    	DownReload(pos);
    	int mid=MID;
    	if(r<=mid) return Query(l,r,ls); else if(l>mid) return Query(l,r,rs); else {return Query(l,mid,ls)+Query(mid+1,r,rs);}
    }
    int main()
    {
    	freopen("haircut.in","r",stdin);
    	freopen("haircut.out","w",stdout);
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%d",&a[i]);
    	for(int i=0;i<=n;i++)
    		lastchange[i]=n+1;
    	long long rp=0;
    	BuildTree(0,n,1);
    	for(int i=n;i>=1;i--)
    	{
    //		for(int j=0;j<a[i];j++)
    //		{
    //			rp+=table[j];
    //			sum[j]+=table[j];
    //		}
    		rp+=Query(0,a[i]-1,1);
    		long long v=Query(a[i]+1,n,1);
    		sum[a[i]]+=table[a[i]]*(v-lastchangev[a[i]]);
    		Update(a[i],1);
    		lastchange[a[i]]=i;
    		lastchangev[a[i]]=v;
    		table[a[i]]++;
    	}
    	for(int i=0;i<n;i++)
    	{
    		sum[i]+=table[i]*(Query(i+1,n,1)-lastchangev[i]);
    	}
    	for(int i=n-1;i>=0;i--)
    	{
    //		printf("[%d: %d] ",i,sum[i]);
    		rp-=sum[i];
    //		printf("%d
    ",rp);
    		sta.push(rp);
    	}
    	while(!sta.empty()) {printf("%lld
    ",sta.top());sta.pop();}
    	return 0;
    }
    
    无特别声明的情况下,本文为原创文章,允许转载,采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
    在声明禁止转载的情况下,请勿转载;若本文章为转载的文章,版权归原作者所有。
    如果您觉得本文写得好,请点击下方的推荐按钮~若您有任何建议和指正,请在下方留言,对于您的指正将不胜感激。
  • 相关阅读:
    Portable Executable 可移植可执行
    汇编跳转指令
    Java中this和super
    Java成员变量和类变量
    EasyUI select
    JAVA虚函数
    感想
    函数
    ueditor
    gitlab简介配置和参数修改
  • 原文地址:https://www.cnblogs.com/ksyx/p/USACO-Open20-Hair-Cut-Solution.html
Copyright © 2020-2023  润新知