CodeTON Round 7(D、E)

D - Ones and Twos 

        题意:给你一个长度为 n 的 数组 a ,其中每个元素都是 1 或 2。请处理以下两种类型的询问

"1 s":检查是否存在 a的子数组  ,其总和等于 s。

"2 i v":将 ai改为v。

如果数组 b 是数组 a 的子数组,那么 b 可以从 a 中通过删除开头的几个(可能是零个或全部)元素和结尾的几个(可能是零个或全部)元素得到。具体来说,一个数组就是它本身的一个子数组。

        思路:由于是单点修改,可以发现整个数组的和是很容易维护的。假设数组和为eq?sum,对于一个查询而言,我们所需要做的就是看从两边能否拿出eq?sum-s大小的数。

        由于只存在1或者2,现考虑拿出偶数的情况:有如下策略,首先两边如果有2,那么就拿2。如果没有2,那就到另一边拿,直到两边都是1为止。然后再将两个1同时拿走,再循环上述操作。因此可以发现的是eq?%5B0%2Csum%5D之间的任意偶数都是能取到的。接下来考虑拿出奇数的情况,奇数就必须要拿到一个1,再拿完1以后按照偶数的操作继续进行下去。因此最小的奇数应该是看左右两边到第一个1的和的最小值。此后更大的奇数也同样能被取到。

        如何去找两边最近的1,可以直接用set来维护每个1的位置,然后访问起点和终点即可。

        

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 5e05+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
int a[N];
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
void solve() 
{
	cin >> n >> m;
	int sum = 0;
	setst;
	for(int i = 1 ; i <= n ; i ++){
		cin >> a[i];
		if(a[i] == 1){
			st.insert(i);
		}
		sum += a[i];
	}
	for(int i = 0 ; i < m ; i ++){
		int op;
		cin >> op;
		if(op == 1){
			int x;
			cin >> x;
			int ne = sum - x;
		//	cout << ne << endl;
			if(ne < 0){
				cout << "NO\n";
			}
			else if(ne % 2 == 1){
				if(!st.empty()){
					auto it = st.begin();
					auto tt = st.rbegin();
					int x = *it;
					int y = *tt;
					int minn = min((x - 1) * 2 , (n - y) * 2) + 1;
					if(ne < minn){
						cout << "NO\n";
					}
					else{
						cout <<"YES\n";
					}
				}
				else{
					cout <<"NO\n";
				}
			}
			else{
				cout << "YES\n";
			}
		}
		else{
			int p , x;
			cin >> p >> x;
			sum -= a[p];
			if(a[p] == 1){
				st.erase(p);
			}
			a[p] = x;
			sum += a[p];
			if(a[p] == 1){
				st.insert(p);
			}
		}
	}
}            

int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

E题应该是把路径转化成一个区间,然后看区间内包含了多少个区间...之后再补4

E - Permutation Sorting 

        题意:给你一个排列a ,大小为 n 。 a 大小为 n 。如果满足 ai=i ,我们称索引 i 为好索引。每秒钟后,我们会将所有不好的索引向右旋转一个位置。

        形式上让 s1,s2,…,sk为a中不好的索引,并按递增顺序排列。即eq?s_%7Bj%7D%20%3C%20s_%7Bj%20+%201%7D ,如果索引 i 不好,那么存在 j ,这样 eq?s_%7Bj%7D = i。从1~k,令eq?a_%7Bs_%7Bi%20mod%20k%20+%201%7D%7D%20%3D%20a_%7Bs_%7Bi%7D%7D

        对于从 1 到 n 的每个 i ,找出索引 i 第一次变为好的时候。

        思路:题目有点不说人话,贴一遍样例

6

2 1 4 6 5 3

        在第二个测试案例中, 5 已经处于正确的位置,因此索引 5 在 0 秒时变为有效。 1 秒后,将对 s=[1,2,3,4,6] 进行循环移位,得到数组 a=[3,2,1,4,5,6] 。请注意,索引 2 、 4 和 6 在 1 秒变为有效。 2 秒后,将对 s=[1,3] 进行循环移位,得到数组 a=[1,2,3,4,5,6] 。注意,索引 1 和 3 在 2 秒时变为有效。

        可以看到,其实在一开始,数组中每个位置需要到达的地方都是确定的了,例如该样例的2需要从第一位跑到第二位,由于是循环数组,我们可以开一个2*n的数组来避免循环,则1需要跑到第7位。考虑将所有数需要走的位置转化为区间。

        如果不考虑索引变好,那么使得每个数变成好索引的轮次为(终点 - 起点)。即数字2需要一轮,数字1需要6轮。接下来考虑索引会变好,即相当于从数组中拿去了该数。例如数字4,只需要一轮即可变为好索引,那么接下来整个数组大小变成5,相当于数字1只需要从第二位跑到第六位即可。也就是说:如果原本某个数所需要走的区间内存在了别的数的区间,那么他所需要的轮次也会减少。因此对于一个数eq?x而言,他所需要的轮次为:(区间终点 - 区间起点 - 区间内的小区间个数)。

        如果我们只记录每个数本身的区间是否会有问题呢,我们观察样例中的数字3,他所走的区间为[6,9],而且其他数构成的区间没有在[6,9]里面的,所以答案会求出3,但是实际上是2,因为其实在3的路径上,2已经是一个好索引了,需要答案减1。那么如何去表示2这个区间,我们不光需要记录他本身的区间[1,2],还需要记录一下[7,8],这样才能影响到后面循环上来的数。因此对于eq?a_%7Bi%7D%20%5Cgeq%20i的数而言,不光需要记录一下eq?%5Bi%20%2C%20a%5Bi%5D%5D 还需要记录eq?%5Bi%20+%20n%20%2C%20a%5Bi%5D%20+%20n%5D

        接下来考虑如何统计区间,可以先对所有区间进行一个按照左端点排序,然后统计一下所有区间右端点小于等于某个数的个数。例如某个区间[x,y]统计他的时候就将树状数组大于等于y的所有值都加一。然后我们遍历区间左端点1~n,每查询完一个区间,就将其删除,这样便能确保当前统计区间的左端点都是大于等于当前查询的左端点 。每一轮查询的答案就是右端点-左端点-区间右端点小于右端点的个数。

        

// Problem: E. Permutation Sorting
// Contest: Codeforces - CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!)
// URL: https://codeforces.com/contest/1896/problem/E
// Memory Limit: 256 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include 
using namespace std;
#define LL long long
#define pb push_back
#define x first
#define y second 
#define endl '\n'
const LL maxn = 4e05+7;
const LL N = 1e06+10;
const LL mod = 1e09+7;
const int inf = 0x3f3f;
const LL llinf = 5e18;
typedef pairpl;
priority_queue, greater >mi;//小根堆
priority_queue ma;//大根堆
LL gcd(LL a, LL b){
	return b > 0 ? gcd(b , a % b) : a;
}

LL lcm(LL a , LL b){
	return a / gcd(a , b) * b;
}
int n , m;
int a[N];
void init(int n){
	for(int i = 0 ; i <= n ; i ++){
		a[i] = 0;
	}
}
struct BIT{//Binary indexed Tree(树状数组)
	int n;
	vector tr;
	BIT(int n) : n(n) , tr(n + 1 , 0){
	}
	int lowbit(int x){
		return x & -x;
	}
	void modify(int x , int modify_number){
		for(int i = x ; i <= n ; i += lowbit(i)){
			tr[i] += modify_number;
		}
	}
	void modify(int l , int r , int modify_number){
		modify(l , modify_number);
		modify(r + 1 , -modify_number);
	}
	int query(int x){
		int res = 0;
		for(int i = x ; i ;  i -= lowbit(i))
			res += tr[i];
		return res;
	}
	int query(int x , int y){
		return query(y) - query(x);
	}
};
void solve() 
{
	cin >> n;
	set< pair >p;
	BIT bit(2 * n + 5);
	for(int i = 1 ; i <= n ; i ++)
		cin >> a[i];
	for(int i = 1 ; i <= n ; i ++){
		if(a[i] >= i){
			p.insert({i , a[i]});
			p.insert({i + n , a[i] + n});
			bit.modify(a[i] , 1);
			bit.modify(a[i] + n , 1);
		}
		else if(a[i] < i){
			p.insert({i , n + a[i]});
			bit.modify(n + a[i] , 1);
		}
	}	
	vectorans(n + 2);
	for(auto it : p){
		if(it.x > n){
			break;	
		}
		int id = (it.y - 1) % n + 1;
	//	cout << it.x << it.y< 1){
			ans[id] = it.y - it.x - bit.query(it.y - 1);
		}
		else if(it.y - it.x == 1){
			ans[id] = 1;
		}
		else{
			ans[id] = 0;
		}
		bit.modify(it.y , -1);
	}
	for(int i = 1 ; i <= n ; i ++){
		cout << ans[i] <<" ";
	}
	cout << endl;
}            
int main() 
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cout.precision(10);
    int t=1;
	cin>>t;
    while(t--)
    {
    	solve();
    }
    return 0;
}

 

 

你可能感兴趣的:(算法)