【CF 706】(C.Hard problem) + (D.Vasiliy's Multiset) + (E.Working routine)【最短路、01字典树、十字链表模拟】

C. Hard problem

题意 n n 个字符串,顺序固定,每个字符串可以进行一次反转,反转代价为 c i c_i ,现要求将所有字符串按照字典序排列,原有顺序不能改变,只能够进行反转,问最少需要多少代价可以让字符串按字典序排列。如果不能,输出 1 -1
思路:对于每一个字符串建立两个点,一个是原状态 s i s_i ,一个是翻转状态 s i s_i' 。如果 s i s_i s i + 1 s_{i+1} 小,则将两点连单向边,边权为 0 0 ; 如果 s i s_i s i + 1 s_{i+1}' 小,则将两点连单向边,边权为 c i + 1 c_{i+1} 。然后对于 s i s_i' 进行相同判断与连边操作。
最后建立一个源点连接 s 1 s_1 s 1 s_1' ,连接 s 1 s_1' 的边权为 c 1 c_1 ,建立一个汇点,使 s n s_n s n s_n' 连接汇点,此处边权为 0 0 。从源点向汇点跑最短路,跑不到则输出 1 -1 ,否则输出答案即可。
反思:代码要注意细节,比赛的时候因为 d i j dij 函数返回值为 i n t int 一直 w a wa 在第 18 18 个点,一直到比赛快结束了才找到问题,一定要引以为戒!
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 5*1e6+100;
const int M = 5*1e6+100;
// const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll inf = 5*1e16;
const db EPS = 1e-9;
using namespace std;

struct Edge{
   int to,next;
   ll w;
}e[M];
int head[N],tot,n,vis[N];
ll c[N],dis[N];

void add(int x,int y,ll w){
   e[++tot].to = y, e[tot].next = head[x], e[tot].w = w, head[x] = tot;
}

priority_queue< pair<ll,int> > q;

ll dijkstra(int s)
{
   while(q.size()) q.pop();
   memset(vis,0,sizeof vis);
   rep(i,0,2*n+2) dis[i] = inf;
   dis[s] = 0;
   q.push(make_pair(0ll,s));
   while(q.size())
   {
   	int x = q.top().second;
   	q.pop();
   	if(vis[x]) continue;
   	vis[x] = 1;	//只有从队列中弹出时才是更新好了这个点,此时才可以更新vis,不能在加入的时候就更新vis
   	for(int i = head[x]; i; i = e[i].next)
   	{
   		int y = e[i].to;
   		ll z = e[i].w;
   		if(dis[y] > (ll)(dis[x] + z)){
   			dis[y] = dis[x]+z;
   			q.push(make_pair(-dis[y],y));
   		}
   	}
   }
   if(dis[n*2+2] == inf) return -1;
   return dis[2*n+2];
}

int main()
{
   __; cin >> n; tot = 1;
   rep(i,1,n) cin >> c[i];
   string b1,b2;
   int r1,r2;
   rep(i,1,n){
   	string s1,s2;
   	cin >> s1; s2 = s1;
   	reverse(s2.begin(),s2.end());
   	if(i == 1){
   		b1 = s1, b2 = s2;
   		r1 = i, r2 = i+n;
   		add(2*n+1,r1,0);
   		add(2*n+1,r2,c[1]);
   	}
   	else{
   		if(b1 <= s1) add(r1,i,0ll);
   		if(b1 <= s2) add(r1,i+n,c[i]);
   		if(b2 <= s1) add(r2,i,0ll);
   		if(b2 <= s2) add(r2,i+n,c[i]);
   		b1 = s1, b2 = s2;
   		r1 = i, r2 = i+n;	
   	}
   }
   add(n,2*n+2,0);
   add(2*n,2*n+2,0);
   ll x1 = dijkstra(2*n+1);
   cout << x1 << endl;
   return 0;
}

D.Vasiliy’s Multiset

题意:维护一个 M u l t i s e t Multiset ,支持三种操作。① 加入一个数 ② 删除一个数 ③ 给出一个 x x ,在 M u l t i s e t Multiset 中找到一个 y y ,使得 x   x o r   y x\ xor\ y 最大。注意, M u l t i s e t Multiset 中初始存在 0 0 。(操作次数 2 1 0 5 \leq 2*10^5 ,数值 1 0 9 \leq 10^9 )
思路:可以发现本题的关键之处在于如何找到一个点 y y x x 的异或值最大,而异或值最大无非就是找到一个数,使得在二进制情况下,从最高位向最低位遍历, x x 1 1 处, y y 0 0 x x 0 0 处, y y 1 1 ,位数越高优先级越高。
因此不难想到维护 01 01 字典树,由于数值最大为 1 0 9 10^9 ,因此字典树深度最深为 32 32 ,即维护 32 32 个二进制位。字典树中每个节点的 f l a g flag 记录这个节点被遍历了多少次,插入或删除一个元素,即将元素二进制状态下经过的节点 f l a g flag + 1 1 / - 1 1 。最后考虑求与 x x 异或值最大的 y y ,将 x x 的二进制形式输入字典树,每一层尽量走与 x x 二进制位置不同的那个节点,最后能够找到一条路径,将路径上所有二进制位加起来,即得到了 y y

在这里插入图片描述

代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const int M = 1e5+100;
const int charset = 3;
const int max_node = 2*1e5*32+100;
const db EPS = 1e-9;
using namespace std;

int q;
char op[10];
int ss[40];

struct Trie
{
	int tot,root,child[max_node][charset];  //root:根节点  tot:节点编号
	//child[i][j]=k,表示编号为i的节点的第j个孩子是编号为k的节点
	int flag[max_node];   //是否以某一个字符为结束
	Trie()
	{
//		memset(child,0,sizeof(child));
		memset(flag,0,sizeof(flag)); 
		root = tot = 1;  //根节点编号为1 
	}	
	void mem()  //将字典树初始化 
	{
		memset(child,0,sizeof(child));
		memset(flag,0,sizeof(flag)); 
		root = tot = 1;  //根节点编号为1 
	}
	void dele()
	{
		int cur = root;
		for(int i = 1; i<=32; i++)
		{
			int x = ss[i];
			cur = child[cur][x];
			flag[cur]--;
		}
	}
	void insert()
	{
		int cur = root;
		for(int i = 1; i<=32; i++)
		{
			int x = ss[i];
			// LOG2("i",i,"x",x);
			// LOG1("cur_y",child[cur][1]);
			if(child[cur][x] == 0)
				child[cur][x] = ++tot;
			cur = child[cur][x];
			flag[cur]++;
		}
	}
	int query()
	{
		int tp = 0;
		int cur = root;
		for(int i = 1; i <= 32; i++)
		{
			int x = ss[i],y;
			if(x == 1) y = 0;
			else y = 1;
			// LOG3("i",i,"x",x,"y",y);
			// LOG1("cur_y",child[cur][y]);
			if(child[cur][y] != 0 && flag[child[cur][y]] >= 1){
				if(y == 1) tp += (1<<(32-i));
				cur = child[cur][y];
				// printf("in y\n");
			}
			else{
				if(x == 1) tp += (1<<(32-i));
				cur = child[cur][x];
				// printf("in x\n");
			}
		}
		return tp;
		//查询单词时应该 return flag[cur]; 
	}
}tre;

int main()
{
	scanf("%d",&q);
	memset(ss,0,sizeof ss);
	tre.insert();
	rep(i,1,q){
		scanf("%s",op);
		if(op[0] == '+'){
			int xx, pos = 32;
			scanf("%d",&xx);
			memset(ss,0,sizeof ss);
			while(xx){
				if(xx&1) ss[pos] = 1;
				xx /= 2;
				pos--;
			}
			// rep(i,1,32) printf("%d ",ss[i]);
			// printf(" ");
			tre.insert();
		}
		else if(op[0] == '-'){
			int xx, pos = 32;
			scanf("%d",&xx);
			memset(ss,0,sizeof ss);
			while(xx){
				if(xx&1) ss[pos] = 1;
				xx /= 2;
				pos--;
			}
			tre.dele();	
		}
		else{
			int xx, pos = 32, base;
			scanf("%d",&xx);
			base = xx;
			memset(ss,0,sizeof ss);
			while(xx){
				if(xx&1) ss[pos] = 1;
				xx /= 2;
				pos--;
			}
			int yy = tre.query();
			// LOG2("base",base,"yy",yy);
			ll ans = ((ll)yy)^((ll)base);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

E.Working routine

题意:给出一个 n m n*m 的矩阵, q q 次操作,每次操作在矩阵中指定两个不重叠且不接触的小矩阵,将两个矩阵中的对应元素互换。在 q q 次操作之后,输出最后的结果矩阵。 ( 2 n , m 1 0 3 , 1 q 1 0 4 ) (2\leq n,m\leq 10^3,1\leq q\leq 10^4)
思路:此题是子矩阵交换,我们可以先考虑一维状态下的子序列交换,区间 [ a , b ] [a,b] [ c , d ] [c,d] 交换,如下图所示。每个节点存储右指针,只需将节点[a-1]与[c-1]以及[b]与[d]交换右指针即可。此处需注意,如果两个区间重叠或相互接触,则会发生错误,可以自行手动模拟一下。

在这里插入图片描述

知道了一维情况下的操作,我们可以考虑二维情况下如何操作。首先每个节点需要维护向右与向下的指针,然后我们来考虑下图情况,两个子矩形相互交换元素。我们可以发现决定这个矩形具体所在位置只是A1、A3区域的向下指针与A2、A4区域的向右指针。
因此我们只需将A1、A3与B1、B3的向下指针进行交换,A2、A4与B2、B4的向右指针进行交换即可完成题目要求。注意如果两个矩形有可能相交或接触,则此算法不可行。

在这里插入图片描述

代码
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define LOG1(x1,x2) cout << x1 << ": " << x2 << endl;
#define LOG2(x1,x2,y1,y2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << endl;
#define LOG3(x1,x2,y1,y2,z1,z2) cout << x1 << ": " << x2 << " , " << y1 << ": " << y2 << " , " << z1 << ": " << z2 << endl;
typedef long long ll;
typedef double db;
const int N = 2*1e6+100;
const int M = 1e5+100;
const db EPS = 1e-9;
using namespace std;

int n,m,q;
struct Node{
	int r,d,v;
}t[N];

int Pos(int x,int y){
	x++, y++;
	return (x-1)*(m+1)+y;
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	rep(i,1,n)
		rep(j,1,m) scanf("%d",&t[Pos(i,j)].v);
	//一共n+1行,m+1列,从(0,0)开始编号
	rep(i,0,n)
		rep(j,0,m){
			t[Pos(i,j)].r = Pos(i,j+1);
			t[Pos(i,j)].d = Pos(i+1,j);
		}
	rep(i,1,q){
		int a,b,c,d,h,w;
		scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&h,&w);
		int x = 1,y = 1,u,v;
		rep(j,1,a-1) x = t[x].d;
		rep(j,1,b-1) x = t[x].r;
		rep(j,1,c-1) y = t[y].d;
		rep(j,1,d-1) y = t[y].r;
		u = x, v = y; //先往下再往右,左下周长部分
		rep(j,1,h){
			u = t[u].d, v = t[v].d;
			swap(t[u].r,t[v].r);
		}
		rep(j,1,w){
			u = t[u].r, v = t[v].r;
			swap(t[u].d,t[v].d);
		}
		u = x, v = y; //先往右再往下,右上周长部分
		rep(j,1,w){
			u = t[u].r, v = t[v].r;
			swap(t[u].d,t[v].d);
		}
		rep(j,1,h){
			u = t[u].d, v = t[v].d;
			swap(t[u].r,t[v].r);
		}
	}
	int x = 1;
	x = t[x].d, x = t[x].r;
	rep(i,1,n){
		int y = x;
		rep(j,1,m){
			printf("%d",t[y].v);
			y = t[y].r;
			if(j == m) printf("\n");
			else printf(" ");
		}
		x = t[x].d;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/89084561