CSP-S模拟13

又是模拟退役的一天

A. 排序

死因 : 输出没有让前面小于后面

通过找规律发现交换两个数值相邻的一定可以

原因是这样保证每次操作只减少一个逆序对

code
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c >'9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
	return x;
}
const int maxn = 1005;
int a[maxn], n, b[maxn];
bool mp[maxn][maxn];
int pos[maxn];
int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[i] = a[i];
	int m = 0 ;
	for(int i = 1; i <= n; ++i){
		for(int j = i + 1; j <= n; ++j)
			if(a[i] > a[j]){
				++m; mp[i][j] = mp[j][i] = 1;
			}
	}
	for(int i = 1; i <= n; ++i)pos[a[i]] = i;
	printf("%d\n",m);
	while(m){
		for(int i = 1; i < n; ++i){
			if(mp[pos[i]][pos[i + 1]]){
				mp[pos[i]][pos[i + 1]] = mp[pos[i + 1]][pos[i]] = 0;
				printf("%d %d\n", min(pos[i], pos[i + 1]), max(pos[i + 1], pos[i]));
				swap(pos[i], pos[i + 1]);
				--m;
			}
		}
	}
	return 0;
}

目前写法在 \(cf\) 上会 \(TLE\) ,等我褐个正解来

B. Xorum

死因:没有乱搞,啥都不会

目前写出的解法,随机化找互质的数对,然后解 \(ax + by = 1\),搞出 \(ax\)\(by\) 看看是否需要都加上一个奇数即可

\(cf\) 上被卡的死死的,正解稍后再说

upd : cf上需要离散化,然后我写挂了..

code
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 100005;
ll now[maxn];
set<ll> s;
int top;
bool fl = 0;
struct node{ll opt, x, y;};
vector<node>v;
void add(ll x, ll y){
	if(s.count(x + y))return;
	now[++top] = x + y; s.insert(x + y);
	v.push_back({1, x, y});
}
void xorr(ll x, ll y){
	if(s.count(x xor y))return;
	now[++top] = x xor y;s.insert(x xor y);
	v.push_back({2, x, y});
}
int prime(){
	int o = now[top] & 1;
	for(int i = top - 1; i >= 0; --i){
		if((o xor (now[i] & 1)) && __gcd(now[top], now[i]) == 1){
			return i;
		}
	}
	return -1;
}
mt19937 rd((ull)(new char) * (ull)(new char));
int srd(int l, int r){
	uniform_int_distribution<>d(l, r);
	return d(rd);
}
void exgcd(ll a, ll b, ll &x, ll &y){
	if(!b){
		x = 1; y = 0;
		return;
	}
	exgcd(b, a % b, y, x);
	y = y - a / b * x;
}
void getpow(ll x, ll y){
	ll ans = 0; y;
	for(; y; y >>= 1, add(x, x), x = x + x)if(y & 1)add(ans, x), ans = ans + x;
}
int main(){
	scanf("%lld",&now[0]); s.insert(now[0]);
	int pos = -1, cnt = 1;
	while((pos = prime()) == -1){
		if(srd(1, 5) != 1)add(now[srd(0, top)], now[srd(0, top)]);
		else xorr(now[srd(0, top)], now[srd(0, top)]);
	}
	ll a = now[pos], b = now[top], x = 0, y = 0;
	exgcd(a, b, x, y);
	ll nx = (x % b + b) % b;
	ll k = (nx - x) / b;
	ll ny = y - k * a;
	xorr(now[0], now[0]);
	getpow(a, nx); getpow(b, -ny);
	ll ax = a * nx, by = -b * ny;
	if((ax xor by) == 1){
		xorr(ax, by);
	}else {
		add(ax, now[0]); add(by, now[0]);
		xorr(ax + now[0], by + now[0]);
	}
	printf("%ld\n",v.size());
	for(node p : v){
		printf("%lld %lld  %lld\n",p.opt, p.x, p.y);		
	}
	return 0;
}

发现可以用左移和异或消去最高位的 \(1\),然后就可以直接搞了

code

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
struct node{ll opt, x, y;};
vector<node>v;
void add(ll x, ll y){v.push_back({1, x, y});}
void xorr(ll x, ll y){v.push_back({2, x, y});}
int solve(ll x){
	ll now = x, y = x >> 1;
	while(y){
		add(now, now);
		now <<= 1;
		y >>= 1;
	}
	ll z = x ^ now; xorr(x, now);
	ll a = z + now; add(z, now);
	ll b = now + now; add(now, now);
	ll c = a ^ b; xorr(a, b);
	ll d = c ^ x; xorr(c, x);
	while(now != (now & - now)){
		if(now & d){
			xorr(now, d);
			now ^= d;
		}
		add(d, d);
		d <<= 1;
	}
	xorr(x, now);
	return x xor now;
}
int main(){
	ll x; scanf("%lld",&x);
	while(x != 1)x = solve(x);
	printf("%ld\n",v.size());
	for(node p : v){
		if(p.opt & 1)printf("%lld + %lld\n",p.x, p.y);		
		else printf("%lld ^ %lld\n",p.x, p.y);		
	}
	return 0;
}

C. 有趣的区间问题

死因 int j = a[i]

分治处理,枚举最大值最小值在区间哪一侧分四种情况讨论,处理时候使用双指针扫

对于最大最小值不在区间一侧的,可以将指针之间的部分加入桶,桶的下标是 \(popcount\),对应查询即可

对于重复元素,最大值最小值在一侧的选择一种带等号,不在同一侧的也选择一个带等号

好像讲题时候我插的那一嘴还说错了,不过应该没人听吧?

code


#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll x = 0; char c = getchar();
	while(c < '0' || c >'9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
	return x;
}
const int maxn = 1000005;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll a[maxn];
int pop[maxn], n;
ll mi[maxn], mx[maxn];
int mip[maxn], mxp[maxn];
int cnp[maxn];
ll solve(int l, int r){
	if(l == r)return 1;
	if(l > r)return 0;
	int mid = (l + r) >> 1;
	ll ans = 0;
	ll nmi = a[mid], nmx = a[mid];
	int nmip = mid, nmxp = mid;
	for(int i = mid; i >= l; --i){
		if(nmi > a[i]){
			nmi = a[i];
			nmip = i;
		}
		if(nmx < a[i]){
			nmx = a[i];
			nmxp = i;
		}
		mi[i] = nmi; mx[i] = nmx;
		mip[i] = nmip; mxp[i] = nmxp;
	}
	nmi = nmx = a[mid + 1]; nmip = nmxp = mid + 1;
	for(int i = mid + 1; i <= r; ++i){
		if(nmi > a[i]){
			nmi = a[i];
			nmip = i;
		}
		if(nmx < a[i]){
			nmx = a[i];	
			nmxp = i;
		}
		mi[i] = nmi; mx[i] = nmx;
		mip[i] = nmip; mxp[i] = nmxp;
	}
	{
		int pr = mid;
		for(int pl = mid; pl >= l; --pl){
			if(pop[mip[pl]] != pop[mxp[pl]])continue;
			while(mi[pr + 1] >= mi[pl] && mx[pr + 1] <= mx[pl] && pr < r)++pr;
			ans += pr - mid;
		}
	}
	{
		int pl = mid + 1;
		for(int pr = mid + 1; pr <= r; ++pr){
			if(pop[mip[pr]] != pop[mxp[pr]])continue;
			while(mi[pl - 1] > mi[pr] && mx[pl - 1] < mx[pr] && pl > l)--pl;
			ans += mid + 1 - pl;
		}
	}
	{
		int pl = mid, pr = mid;
		for(int i = mid; i >= l; --i){
			while(mx[pr + 1] <= mx[i] && pr < r)++pr, ++cnp[pop[mip[pr]]];
			while(pl < pr && mi[pl + 1] >= mi[i])++pl, --cnp[pop[mip[pl]]];
			ans += cnp[pop[mxp[i]]];
		}
		while(pl < pr)++pl, --cnp[pop[mip[pl]]];
	}
	{
		int pl = mid + 1, pr = mid + 1;
		for(int i = mid + 1; i <= r; ++i){
			while(mx[pl - 1] < mx[i] && pl > l)--pl, ++cnp[pop[mip[pl]]];
			while(pl < pr && mi[pr - 1] > mi[i])--pr, --cnp[pop[mip[pr]]];
			ans += cnp[pop[mxp[i]]];
		}
		while(pl < pr)--pr, --cnp[pop[mip[pr]]];
	}
	ans += solve(l, mid) + solve(mid + 1, r);
	return ans;
}

int main(){
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i){
		ll j = a[i];
		while(j){
			++pop[i];
			j -= (j & - j);
		}
	}
	printf("%lld\n",solve(1, n));
	return 0;
}

D. 无聊的卡牌问题

死因,没有死

跑个栈就能定下来三元组,然后存在先后的限制关系,所以我们建个图

把三元组左侧元素看做左括号,右侧看做右括号,那么这就是括号匹配,只有包含和不想交的关系

那么我们建出来的其实是一个森林

考虑如何操作

因为最后一次操作是后手,所以我们需要保留一个后手的根

考虑操作过程,我们肯定是选择一个叶子删掉,并且使颜色交替

通过一些证明可以得到删去任何一个叶子都可以

那么我们可以跑拓扑,每次交替取先手后手,可以用两个队列或者双端队列维护一下

记得保留后手的根,可以先预处理出有几个后手的根,拓扑时如果现在出队的是最后一个就跳过他,把他最后输出

code
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
	int x = 0; char c = getchar();
	while(c < '0' || c >'9')c = getchar();
	do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >='0');
	return x;
}
const int maxn = 1500;
bool vis[maxn];
struct node{int col, x, y, z;}d[maxn];
int  cnt, rd[maxn], fa[maxn];
int sta[maxn], nt[maxn], top;
int n, rem[maxn];
deque<int>q;
void print(int x){
	printf("%d %d %d\n", d[x].x, d[x].y, d[x].z);
}

int main(){
	n = read();
	for(int i = 1; i <= n + n + n; ++i)vis[read()] = 1;
	for(int i = 1; i <= n * 6; ++i){
		if(top >= 2 && vis[sta[top]] == vis[sta[top - 1]] && vis[i] == vis[sta[top]]){
			nt[sta[top - 1]] = ++cnt;
			nt[sta[top]] = cnt;
			nt[i] = cnt;
			d[cnt] = {vis[i], sta[top - 1], sta[top], i};
			top -= 2;
		}else sta[++top] = i;
	}
	for(int i = 1; i <= n * 6; ++i){
		if(top >= 2 && vis[sta[top]] == vis[sta[top - 1]] && vis[i] == vis[sta[top]])top -= 2;
		else{
			if(top && nt[i] != nt[sta[top]]){
				fa[nt[i]] = nt[sta[top]];
				++rd[nt[sta[top]]];
			}
			sta[++top] = i;
		}
	}
	int crt = 0;
	for(int i = 1; i <= cnt; ++i)if(fa[i] == 0 && d[i].col == 0)++crt;
	for(int i = 1; i <= cnt; ++i)if(rd[i] == 0){
		if(d[i].col)q.push_front(i);
		else q.push_back(i);
	}
	int rt = 0, round = 0;
	while(!q.empty()){
		++round;
		int x = 0;
		if(round & 1){
			x = q.front();
			q.pop_front();
		}else{
			x = q.back();
			q.pop_back();
			if(!fa[x] ){
				--crt;
				if(crt == 0){
					rt = x;
					if(q.empty())break;
					x = q.back();
					q.pop_back();
				}
			}
		}
		rem[round] = x;
		--rd[fa[x]]; 
		if(rd[fa[x]] == 0){
			if(d[fa[x]].col)q.push_front(fa[x]);
			else q.push_back(fa[x]);
		}
	}
	if(rem[round]) ++round;
	rem[round] = rt;
	for(int i = 1; i <= round; ++i)print(rem[i]);
	return 0;
}
posted @ 2022-09-27 18:39  Chen_jr  阅读(71)  评论(2编辑  收藏  举报