codeforces round 416 div2 补题 CF 811 A B C D E

A. Vladik and Courtesy

水题略过

#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long int LL;
int main()
{
 LL a,b;
 scanf("%I64d%I64d",&a,&b);
 LL num=1;
 while(true)
 	{
 	 if(a<num){printf("Vladik
");return 0;}
 	 a-=num;
 	 //b+=num;
 	 num++;
 	 if(b<num){printf("Valera
");return 0;}
 	 b-=num;
 	 //a+=num;
 	 num++;
	}
 return 0;
} 

B. Vladik and Complicated Book

给定一个序列, 询问子区间【l,r】的第k小数是不是原位置的数。

显然可以用主席树维护,不过这道题数据放水了,用暴力一点的方法应该也能过。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100000 + 5;

int a[N], b[N], rt[N * 20], ls[N * 20], rs[N * 20], sum[N * 20],numm[N];

int n, k, tot, sz, ql, qr, x, q, T;

void Build(int& o, int l, int r){
    if(l>r)return ;
	o = ++ tot;
    sum[o] = 0;
    if(l == r) return;
    int m = (l + r) >> 1;
    Build(ls[o], l, m);
    Build(rs[o], m + 1, r);
}

void update(int& o, int l, int r, int last, int p){
    o = ++ tot;
    ls[o] = ls[last];
    rs[o] = rs[last];
    sum[o] = sum[last] + 1;
    if(l == r) return;
    int m = (l + r) >> 1;
    if(p <= b[m])  update(ls[o], l, m, ls[last], p);
    else update(rs[o], m + 1, r, rs[last], p);
}

int query(int ss, int tt, int l, int r, int k){
    if(l == r) return l;
    int m = (l + r) >> 1;
    int cnt = sum[ls[tt]] - sum[ls[ss]];
    if(k <= cnt) return query(ls[ss], ls[tt], l, m, k);
    else return query(rs[ss], rs[tt], m + 1, r, k - cnt);
}

void work(){
    scanf("%d%d%d", &ql, &qr, &x);
    int xx=x;
    x=(xx-ql+1);
    int ans = query(rt[ql - 1], rt[qr], 1, sz, x);
    if(b[ans]==numm[xx])printf("Yes
");
    	else printf("No
");
}

int main(){//freopen("t.txt","r",stdin);
   T=1; 
    while(T--){
        scanf("%d%d", &n, &q);
        for(int i = 1; i <= n; i ++) scanf("%d", a + i), numm[i]=b[i] = a[i];
        sort(b + 1, b + n + 1);
        sz = unique(b + 1, b + n + 1) - (b + 1);
        tot = 0;
        Build(rt[0],1, sz);
        //for(int i = 0; i <= 4 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d
", i, rt[i], ls[i], rs[i], sum[i]);
        //for(int i = 1; i <= n; i ++)a[i] = lower_bound(b + 1, b + sz + 1, a[i]) - b;
        for(int i = 1; i <= n; i ++)update(rt[i], 1, sz, rt[i - 1], a[i]);
       // for(int i = 0; i <= 5 * n; i ++)printf("%d,rt =  %d,ls =  %d, rs = %d, sum = %d
", i, rt[i], ls[i], rs[i], sum[i]);
        while(q --)work();
    }
    return 0;
}

C. Vladik and Memorable Trip

O(n^2)的线性dp 不要想太多。。(我都想到DAG去了。。。)

代码比较简单,可以直接看懂。

#include <bits/stdc++.h>
using namespace std;

vector <int> ps [5010], cs[5010];
int s[5010] , e[5010];
bool vis[5010];
long long dp [5010];
int main()
{
	int n;
	scanf("%d",&n);
	vector <int> a (n);
	for (int i = 0; i < n; i++) {
		scanf("%d",&a[i]);
		if (!vis[a[i]]) s[a[i]] = i;
		vis[a[i]] = 1;
		e[a[i]] = i;
	}
	for (int i = 0; i < n; i++) vis[a[i]] = 0;
	for (int i = 0; i < n; i++) {
		int mx = i, rx = 0, lj = i;
		for (int j = i; j < n; j++) {
			if (s[a[j]] < i) break;
			lj = j;
			mx = max(mx,e[a[j]]);
			if (!vis[a[j]]) rx ^= a[j];
			vis[a[j]] = 1;
			
			if (mx <= j) {
				ps[i].push_back(j);
				cs[i].push_back(rx);
			}
		}
		for (int k = lj; k >= i; k--) vis[a[k]] = 0;
	}
	for (int i = n-1; i >= 0; i--) {
		dp[i] = dp[i+1];
		for (int j = 0; j < ps[i].size(); j++) {
			dp[i] = max(dp[i],dp[ps[i][j]+1]+cs[i][j]);
		}
	}
	printf("%I64d
",dp[0]);
}

D. Vladik and Favorite Game

 第一步判断一下按钮是否反转即可。剩下的直接暴力求路径,水题。

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define ff first
#define ss second
#define pb push_back

const int N = 100 + 11;

int use[N][N];
char a[N][N],b[5];
vector<pair<int,int> > v,w;
vector<int> vv;

void dfs(int l, int r)
{
    use[l][r]=1;
    v.pb(mp(l,r));
    if (a[l][r]=='F') {w=v; return;}
    if (use[l-1][r]==0&&(a[l-1][r]=='.'||a[l-1][r]=='F')) dfs(l-1,r);
    if (use[l+1][r]==0&&(a[l+1][r]=='.'||a[l+1][r]=='F')) dfs(l+1,r);
    if (use[l][r-1]==0&&(a[l][r-1]=='.'||a[l][r-1]=='F')) dfs(l,r-1);
    if (use[l][r+1]==0&&(a[l][r+1]=='.'||a[l][r+1]=='F')) dfs(l,r+1);
    v.pop_back();
}
int main()
{
    int n,m;
    cin>>n>>m;
    int l,r;
    for (int i=1; i<=n; i++)
    for (int j=1; j<=m; j++)
    {
        cin>>a[i][j];
        if (a[i][j]=='F') {l=i; r=j;}
    }
    b[1]='L';
    b[2]='R';
    b[3]='U';
    b[4]='D';
    dfs(1,1);
    for (int i=1; i<w.size(); i++)
    {
        if (w[i].ff==w[i-1].ff)
        {
            if (w[i].ss==w[i-1].ss-1) vv.pb(1); else vv.pb(2);
        } else
        if (w[i].ff==w[i-1].ff-1) vv.pb(3); else vv.pb(4);
    }
    int u1=0,u2=0;
    int x=1,y=1;
    for (int i=0; i<vv.size(); i++)
    {
        if (vv[i]==2&&u1==0)
        {
            cout<<"R"<<endl;
            int xx,yy;
            cin>>xx>>yy;
            u1=1;
            if (x==xx&&y==yy) {swap(b[1],b[2]); cout<<"L"<<endl; cin>>x>>y;}
        } else
        if (vv[i]==4&&u2==0)
        {
            cout<<"D"<<endl;
            int xx,yy;
            cin>>xx>>yy;
            u2=1;
            if (x==xx&&y==yy) {swap(b[3],b[4]); cout<<"U"<<endl; cin>>x>>y;}
        } else
        {
            cout<<b[vv[i]]<<endl;
            cin>>x>>y;
        }
    }

}

  

E. Vladik and Entertaining Flags

线段树+并查集

给定一个行数为10 列数10w的矩阵,每个方块是一个整数, 给定l和r 求范围内的联通块数量 所谓联通块即数字相等。

考虑用二分的方法合并两个联通块,在合并的时候只需要标记边界的元素属于哪个块即可,不用考虑块内的元素,因为它们不会在将来的合并中使用到。

这样我们就可以做到在常数时间内合并两个块了,这样用线段树维护就没有问题了。

其实想了一下,用倍增的方法也是可以的,dp[i][k]表示从第i列开始 连续2^k列的联通块,复杂度也是O(nlogn)

所以用一个线段树维护所有的[l,r]子块,每次查询线段树即可,合并的时候要重新标号。

重点:对于每个子块,我们只需要知道边界上的元素属于这个子块的哪个联通分量即可(使用并查集维护)

比赛的时候我也是老想块中的元素怎么维护,就觉得二分难搞了,其实完全不需要考虑。。

#include <bits/stdc++.h>

using namespace std;

const int N = 10;
const int M = 100010;

int n, m, q;
int a[N][M];
int state[50], mark[50];

struct Info{
	int cnt;
	int left[N];
	int right[N];

	Info() {
		cnt = 0;
		memset(left, 0, sizeof left);
		memset(right, 0, sizeof right);
	}
} tr[M << 2];

Info join(Info &u, Info &v, int p) {
	Info f;
	f.cnt = u.cnt + v.cnt;
	memset(state, 0, sizeof state);
	for (int k = 0; k < n; k++) {
		if (a[k][p] == a[k][p + 1]) {
			int x = u.right[k];
			int y = v.left[k] + 21;
			while (state[x]) {
				x = state[x];
			}//路径压缩 
			while (state[y]) {
				y = state[y];
			}
			if (x != y) {
				f.cnt--;
				state[y] = x;
			}
		}
	}
	int g = 1;
	memset(mark, 0, sizeof mark);
	for (int k = 0; k < n; k++) {
		int x = u.left[k];
		while (state[x]) {
			x = state[x];
		}
		if (mark[x] == 0) {
			mark[x] = g++;
		}//从头标号 只需记录左边界和右边界 
		f.left[k] = mark[x];//重新标记边界 
		int y = v.right[k] + 21;
		while (state[y]) {
			y = state[y];
		}
		if (mark[y] == 0) {
			mark[y] = g++;
		}
		f.right[k] = mark[y];//重新标记边界 
	}
	return f;
}

void build(int l, int r, int i) {
	if (l == r) {
		for (int k = 0; k < n; k++) {
			if (k == 0 || a[k][l] != a[k - 1][l]) {
				tr[i].left[k] = tr[i].right[k] = ++tr[i].cnt;	
			} else {
				tr[i].left[k] = tr[i].right[k] = tr[i].left[k - 1];
			}
		}
	} else {
		int mid = (l + r) >> 1;
		build(l, mid, i + i);
		build(mid + 1, r, i + i + 1);
		tr[i] = join(tr[i + i], tr[i + i + 1], mid);
	}
	/*
	cout << " + " << l << " " << r << "
";
	for (int k = 0; k < n; k++) {
		cout << tr[i].left[k] << " ";
	}
	cout << "
";
	for (int k = 0; k < n; k++) {
		cout << tr[i].right[k] << " ";
	}
	cout << "
";
	*/
}

void get(int l, int r, int x, int y, int i, Info &res) {
	if (x <= l && r <= y) {
		if (l == x) {
			res = tr[i];
		} else {
			res = join(res, tr[i], l - 1);
		}
	} else {
		int mid = (l + r) >> 1;
		if (mid >= x) {
			get(l, mid, x, y, i + i, res);
		}
		if (mid < y) {
			get(mid + 1, r, x, y, i + i + 1, res);
		}
	}
}

int main() {
	scanf("%d %d %d", &n, &m, &q);
	for (int i = 0; i < n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%d", &a[i][j]);
		}
	}
	build(1, m, 1);
	Info res;
	while (q--) {
		int l, r;
		scanf("%d %d", &l, &r);
		get(1, m, l, r, 1, res);
		printf("%d
", res.cnt);
	}
}

  

原文地址:https://www.cnblogs.com/heisenberg-/p/6914443.html