Codeforces Round #277 (Div. 2)

时间还不错,不过身体好像越来越差了sad

C. Palindrome Transformation

给出一个长度为n的字符串,初始位置在m,接下来对这个串有4种操作,“上下左右”,上:把当前字符变成字典序的下一个字符,如果是‘z'则变成'a',下:把当前字符变成字典序的上一个字符,如果是'a'则变成'z',左:把位置向左移一位,如果到了字符串的第一个位置,则跳到最后一个位置,右:与左相反

样例解释:

8 3
aeabcaez      --->      6

右:当前指针指b      aeabcaez         上:把b换成c    aeaccaez         左:回到a     aeaccaez     左:回到e       aeaccaez            左:回到a     aeaccaez            下:a变z          zeaccaez             共6步变成回文串

解题思路:为了简化问题,我们只看串的一半,如果初始时的位置在串的右半部分,我们就找到它在左边的对称部分开始操作,简化问题嘛。。。

但是我们要证明在右边和在左边操作的代价是一样的,比如这样一个串asdfvpojl,v是对称轴,把j变成s和把s变成j的代价是一样的。。。总之。。。真的是一样的

然后现在有的串是asdf要把它变成目标串pojl假设现在初始位置在s只需要把s到需要改的字符的步数算出来,把现有字符变成目标字符所需的最小变换次数算出来,相加就是结果。(注意细节的处理)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

char S[110000];
int A[110000];
int mindis(char a, char b) {
    if (a > b) swap(a, b);
    int dis_1 = b-a;
    int dis_2 = a+26-b;
    return min(dis_1, dis_2);
}
int main() {
    int len, pos;
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    scanf("%d%d%s",&len,&pos,S);
    pos--;
    for (int i = 0; i < len; i++) {
        if (S[i] != S[len-1-i])
            A[i] = A[len-1-i] = mindis(S[i], S[len-1-i]);
    }
    //for (int i = 0; i < len/2; i++)
    //    printf("%d ", A[i]);
    //    printf("\n");
    int mid = (len+1)/2-1;
    //printf("mid:%d    pos:%d\n", mid, pos);
    if (pos >= mid) pos = len - 1 - pos;
    //printf("new pos : %d\n", pos);
    if (len%2==1) mid--;
    int ll = 0, rr = pos, ans = 0;
    for (int i = mid; i >= pos; i--) if(A[i] != 0) { rr = i; break; }
    for (int i = 0; i <= mid; i++) if (A[i] != 0) {ll = i; break;}
    //printf("l r%d %d\n", ll, rr);
    for (int i = ll; i <= rr; i++)
        if (A[i] != 0) ans += A[i];
    if (ans==0) {printf("%d\n", ans); return 0;}
    if (pos >= rr) 
            ans += pos-ll;
    
    else if (pos <= ll) 
            ans += rr-pos;
    
    else ans += 2*min(rr-pos, pos-ll) + max(rr-pos, pos-ll);
    
    printf("%d\n", ans);
    return 0;
}


D. Valid Sets

我们把n个节点n-1条边的图形叫做树,现在给你一个数字d和一棵由n个节点组成的树,每个节点有值ai,我们称树上点的一个集合S是合法的如果这个集合S满足下面的条件:

1.S是非空的

2.S是联通的

3.max Su - min Sv <= d (u, v在S中)

你的任务是计算这棵树中有多少个合法的集合,模(int)1e9+7

1 4
2 1 3 2
1 2
1 3
3 4

{1},{2},{3},{4},{1,2},{1,3},{3,4}{2,3,4}这8个是 符合样例的情况

题解:(赤裸裸的题解代码⊙﹏⊙b),这题应该算是枚举,枚举每个点(1,2....n),并把这个点当做含有这个点的集合中权值的最小的点。然后我们以这个点为起点做深搜,搜索其他的点,当其它点的权值小于起点的时候,这个点是不符合条件的(我们已经设了起点的权值最小),当一个点的权值减去起点的权值大于d的时候,也是不符合条件的。当搜到的点的权值和起点相等但是标号小于起点标号的时候,也不把这个点计算在内,否则会计算两遍。排除了这些限制之后,用dp[k]来表示在第k个节点处有多少个满足条件的集合,那么dp[v] = dp[v]*(dp[u]+1)。。。再想想(ˇˍˇ),最后把所有的dp值加起来就是结果了,顺便取模.

#include <bits/stdc++.h>
using namespace std;
const int N = 2000+10;
const int MOD = (int)1e9+7;
int vis[N], dp[N], a[N];
vector<int>G[N];
int d, n;

void DFS(int v, int rt) {
    vis[v] = 1, dp[v] = 1;
    for (int i = 0; i < G[v].size(); i++) {
        int u = G[v][i];
        if (!vis[u]) {
            if (a[u] < a[rt] || a[u] > a[rt] + d) continue;
            if (a[u] == a[rt] && u < rt) continue;  // if (a[u] == a[rt] && u > rt) continue;
            DFS(u, rt);
            dp[v] = ((long long)dp[v]*(dp[u]+1) )%MOD;
        }
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    cin >> d >> n;
    for (int i = 1; i <= n; i++)
    cin >> a[i];
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++)
        dp[j] = 0, vis[j] = 0;
        DFS(i, i);
        ans += dp[i];
        if (ans >= MOD) ans -= MOD;
    }
    cout << ans << endl;
    return 0;
}
  


E. LIS of Sequence
给出一个由n个数字构成的序列,把这个序列的元素分为3种:
1.ai不属于最长递增子序列
2.ai至少属于1个但不属于每个最长递增子序列
3.ai属于每个最长递增子序列
输出这个序列中的每个元素所属的类型
4
1 5 2 3 ------>     3 1 3 3
1 5 2 3的最长递增子序列只有 1 2 3,1仅属于它,5不属于它,2,3仅属于它(这个序列中只有一个最长递增子序列)
研究ing
#include <bits/stdc++.h>


using namespace std;

const int maxn = 100010;

int n,qL,qR;
int a[maxn];
int L[maxn],R[maxn],M[maxn*4];
bool ans[maxn];
int num[maxn];

void build(int x,int f,int t) {
	M[x] = 0;
	if (f == t) return;
	int mid = f + t >> 1;
	build(x<<1,f,mid); build((x<<1)+1,mid+1,t);
}

void insert(int x,int f,int t,int d) {
	if (f == t) {
		M[x] = max(M[x],d);
		return;	
	}
	int mid = f + t >> 1;
	if (qL <= mid)
	insert(x<<1,f,mid,d); 
	if (mid < qR)	
	insert((x<<1)+1,mid+1,t,d);
	M[x] = max(M[x<<1],M[(x<<1)+1]);
}

int query(int x,int f,int t) {
	if (qL <= f && t <= qR) return M[x];
	int mid = f + t >> 1,ans = 0;
	if (qL <= mid) ans = max(ans,query(x<<1,f,mid));
	if (mid < qR)  ans = max(ans,query((x<<1)+1,mid+1,t));
	return ans;
}


int main() {
	scanf("%d",&n); 
	int Tmax = 0,td = 0;
	for (int i = 1;i <= n; i++)  scanf("%d",&a[i]),Tmax = max(a[i],Tmax);
	build(1,1,Tmax);
	for (int i = 1;i <= n; i++) {
		qL = 1;qR = a[i]-1;
		L[i] = (qL <= qR ? query(1,1,Tmax):0)+1;	
		qL = qR = a[i];
		insert(1,1,Tmax,L[i]);
		td = max(td,L[i]);
	}	
	build(1,1,Tmax);
	for (int i = 1;i <= n; i++) {
		qL = a[n-i+1]+1; qR = Tmax;
		R[n-i+1] = (qL <= qR ? query(1,1,Tmax):0)+1;
		qL = qR = a[n-i+1];
		insert(1,1,Tmax,R[n-i+1]);
	}
	for (int i = 1;i <= n; i++) {
		if (L[i] + R[i] == td+1) { ans[i] = true; num[L[i]]++; }
	}	
	for (int i = 1;i <= n; i++) 
		if (!ans[i]) printf("1");
		else if (num[L[i]] == 1) printf("3");
		else printf("2");
	printf("\n");
	return 0;
}


 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值