C. Hard problem
题意
:n个字符串,顺序固定,每个字符串可以进行一次反转,反转代价为ci,现要求将所有字符串按照字典序排列,原有顺序不能改变,只能够进行反转,问最少需要多少代价可以让字符串按字典序排列。如果不能,输出−1。
思路
:对于每一个字符串建立两个点,一个是原状态si,一个是翻转状态si′。如果si比si+1小,则将两点连单向边,边权为0; 如果si比si+1′小,则将两点连单向边,边权为ci+1。然后对于si′进行相同判断与连边操作。
最后建立一个源点连接s1与s1′,连接s1′的边权为c1,建立一个汇点,使sn与sn′连接汇点,此处边权为0。从源点向汇点跑最短路,跑不到则输出−1,否则输出答案即可。
反思
:代码要注意细节,比赛的时候因为dij函数返回值为int一直wa在第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 = 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;
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
题意
:维护一个Multiset,支持三种操作。① 加入一个数 ② 删除一个数 ③ 给出一个x,在Multiset中找到一个y,使得x xor y最大。注意,Multiset中初始存在0。(操作次数 ≤2∗105 ,数值 ≤109)
思路
:可以发现本题的关键之处在于如何找到一个点y与x的异或值最大,而异或值最大无非就是找到一个数,使得在二进制情况下,从最高位向最低位遍历,x为1处,y为0,x为0处,y为1,位数越高优先级越高。
因此不难想到维护01字典树,由于数值最大为109,因此字典树深度最深为32,即维护32个二进制位。字典树中每个节点的flag记录这个节点被遍历了多少次,插入或删除一个元素,即将元素二进制状态下经过的节点 flag +1 / -1。最后考虑求与x异或值最大的y,将x的二进制形式输入字典树,每一层尽量走与x二进制位置不同的那个节点,最后能够找到一条路径,将路径上所有二进制位加起来,即得到了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];
int flag[max_node];
Trie()
{
memset(flag,0,sizeof(flag));
root = tot = 1;
}
void mem()
{
memset(child,0,sizeof(child));
memset(flag,0,sizeof(flag));
root = tot = 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];
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;
if(child[cur][y] != 0 && flag[child[cur][y]] >= 1){
if(y == 1) tp += (1<<(32-i));
cur = child[cur][y];
}
else{
if(x == 1) tp += (1<<(32-i));
cur = child[cur][x];
}
}
return tp;
}
}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--;
}
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();
ll ans = ((ll)yy)^((ll)base);
printf("%lld\n",ans);
}
}
return 0;
}
E.Working routine
题意
:给出一个n∗m的矩阵,q次操作,每次操作在矩阵中指定两个不重叠且不接触的小矩阵,将两个矩阵中的对应元素互换。在q次操作之后,输出最后的结果矩阵。(2≤n,m≤103,1≤q≤104)
思路
:此题是子矩阵交换,我们可以先考虑一维状态下的子序列交换,区间[a,b]与[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);
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;
}