zoukankan      html  css  js  c++  java
  • AtCoder Grand Contest 054

    C.Roughly Sorted

    题目描述

    如果一个排列每个位置上的逆序对个数都 (leq k),那么它是好排列。假设你有排列 (P),每次可以交换两个相邻元素,用最小的步数得到好排列 (P')

    现给定 (P')(k),求可能的 (P) 有多少个。

    (nleq 5000)

    解法

    首先考虑知道了排列 (P) 怎么求出排列 (P'),设第 (i) 个位置的逆序对个数是 (x_i),因为一次交换最多让逆序对减一,所以答案下界是 (sum_imax(x_i-k,0)),构造方法是每次找到最小的位置 (i) 满足 (p_{i-1}>p_i),并且 (x_i>k),把这两个位置交换一下,可以证明如果排列不合法一定能找到这样的位置。

    那么考虑用排列 (P') 还原出可能的 (P),首先我声明:如果我们知道了每个位置上的逆序对个数,那么对应的排列是唯一的。所以我们考察调整后的逆序对个数是否合法即可。

    对于 (x_i<k) 的位置是不能主动换的,因为交换让逆序对增加或减小都是不合法的,让他们的逆序对固定即可。对于 (x_i=k) 的位置逆序对是可以任意增加的,但就是不能减小,所以如果我们按照 (i=n...1) 的顺序考虑,我们可以把它右移任意步数,那么乘上 ((n-i+1)) 即可。

    总结

    最优策略问题也可以思考构造答案下界,但是首先一定要想清楚答案下界是什么。

    计数题中的转化要建立起对应关系,比如这题我们建立了逆序对和排列的对应关系,就把问题转化到逆序对上了。

    #include <cstdio>
    const int M = 5005;
    const int MOD = 998244353;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,k,ans=1,p[M],x[M];
    signed main()
    {
    	n=read();k=read();
    	for(int i=1;i<=n;i++)
    		p[i]=read();
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<i;j++)
    			x[i]+=(p[j]>p[i]);
    	for(int i=n;i>=1;i--)
    		if(x[i]==k) ans=1ll*ans*(n-i+1)%MOD;
    	printf("%d
    ",ans); 
    }
    
  • 相关阅读:
    [leetcode] Palindrome Number
    [leetcode] Find Minimum in Rotated Sorted Array
    [leetcode] Word Search
    [leetcode] Construct Binary Tree from Preorder and Inorder Traversal
    [leetcode] Flatten Binary Tree to Linked List
    [leetcode] Recover Binary Search Tree
    [leetcode] Unique Binary Search Trees II
    [leetcode summary] Dynamic Programming
    [leetcode] Generate Parentheses
    [leetcode summary] Backtracing
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15049588.html
Copyright © 2011-2022 走看看