【codeforces 731D】【差分+线段扫描 思维题】80-th Level Archeology【给你n个word,现在要使得word按字典序排列,随便操作多少次,每次可以使每个word+1】
2016-11-01 23:17
519 查看
传送门:http://codeforces.com/contest/731/problem/D
题意:给你n个word,每个word都有若干个字母,现在要使得从上到下的word都必须小于或等于下一个word,你可以操作一次钥匙将所有word的所有字母都加上1,如果>c,就变为1,问是否有解,有的话随意输出,没有的话就输出-1
思路:
如果光暴力复杂度会达到O(nc),下面介绍两个技巧:
1.首先介绍下什么是差分法。比如给你一个长度为n的数组cnt,一开始全是0。现在如果让你从下标2~4的位置都+1,怎么做?cnt[2]++,cnt[5]--
数组变成了0 0 1 0 0 -1 0....,我们再进行for(int i=0;i<n;++i)cnt[i]+=cnt[i-1]; 现在变成了0 0 1 1 1 0 0...是不是2~4都+1了?
总结一下,差分的想法就是在区间a~b中+1,等价于cnt[a]++,cnt[b+1]--,然后再处理一边前缀和就行了,这样可以降低区间加减的复杂度
2.什么是线段扫描法?给你n个区间,问你有没有一个公共的区间是这所有n个区间的公共子区间。
首先把这几个区级排好,然后找一条竖着的直线,从左向右平移,然后看有没有一瞬间这个直线与所有区间都相交,及有n个交点。
这道题怎么应用上面两种技巧呢
首先对于相邻2个word,
1.如果word[1]>word[2],假设对应的那个字母为a[i],b[i](a[i]>b[i])那么我们需要操作钥匙,使得word[1]<word[2],因为前面的都一样,只需要考虑当前位置i,不难得出使用钥匙次数区间(c-a[i]+1,c-b[i]),对这个区间的cnt加1
2.当word[1]<word[2]的时候,同理可以得出使用钥匙次数区间(0,c-b[i])和(c-a[i]+1,c-1),对这个区间的cnt加1(注意如果比完了一直相等但是word[1]的长度>word[2],我们就可以直接输出-1了,因为不管操作多少次,前面的元素肯定相等,但始终word[1]>word[2]),这里我们遇到这种情况可以不使用钥匙,即不对cnt进行操作)
3.word[1]比word[2]短或者完全一样,我们对区间(0, c)的cnt都加1就好了
对区间的cnt加1可以用差分法实现,复杂度为O(1), 最后判断使用钥匙次数0~c-1这个区间中有没有cnt等于比较的次数n-1(这个n-1的得到用到了线段扫描的思想,需要仔细想想),所以如果其中出现始终word[1]>word[2]的情况是不可能有n-1的
代码:
题意:给你n个word,每个word都有若干个字母,现在要使得从上到下的word都必须小于或等于下一个word,你可以操作一次钥匙将所有word的所有字母都加上1,如果>c,就变为1,问是否有解,有的话随意输出,没有的话就输出-1
思路:
如果光暴力复杂度会达到O(nc),下面介绍两个技巧:
1.首先介绍下什么是差分法。比如给你一个长度为n的数组cnt,一开始全是0。现在如果让你从下标2~4的位置都+1,怎么做?cnt[2]++,cnt[5]--
数组变成了0 0 1 0 0 -1 0....,我们再进行for(int i=0;i<n;++i)cnt[i]+=cnt[i-1]; 现在变成了0 0 1 1 1 0 0...是不是2~4都+1了?
总结一下,差分的想法就是在区间a~b中+1,等价于cnt[a]++,cnt[b+1]--,然后再处理一边前缀和就行了,这样可以降低区间加减的复杂度
2.什么是线段扫描法?给你n个区间,问你有没有一个公共的区间是这所有n个区间的公共子区间。
首先把这几个区级排好,然后找一条竖着的直线,从左向右平移,然后看有没有一瞬间这个直线与所有区间都相交,及有n个交点。
这道题怎么应用上面两种技巧呢
首先对于相邻2个word,
1.如果word[1]>word[2],假设对应的那个字母为a[i],b[i](a[i]>b[i])那么我们需要操作钥匙,使得word[1]<word[2],因为前面的都一样,只需要考虑当前位置i,不难得出使用钥匙次数区间(c-a[i]+1,c-b[i]),对这个区间的cnt加1
2.当word[1]<word[2]的时候,同理可以得出使用钥匙次数区间(0,c-b[i])和(c-a[i]+1,c-1),对这个区间的cnt加1(注意如果比完了一直相等但是word[1]的长度>word[2],我们就可以直接输出-1了,因为不管操作多少次,前面的元素肯定相等,但始终word[1]>word[2]),这里我们遇到这种情况可以不使用钥匙,即不对cnt进行操作)
3.word[1]比word[2]短或者完全一样,我们对区间(0, c)的cnt都加1就好了
对区间的cnt加1可以用差分法实现,复杂度为O(1), 最后判断使用钥匙次数0~c-1这个区间中有没有cnt等于比较的次数n-1(这个n-1的得到用到了线段扫描的思想,需要仔细想想),所以如果其中出现始终word[1]>word[2]的情况是不可能有n-1的
代码:
#include <bits/stdc++.h> using namespace std; #define rep(i,k,n) for(int i=k;i<=n;i++) const int N=5e5+10; std::vector<int> words ; int cnt[2 * N], n, c;//cnt表示转几次是否满足要求 void cal(int a, int b){ int id = 0;//id表示失配位置 while(id < words[a].size() && id < words[b].size()){ if(words[a][id] != words[b][id])break; id++; } if(id < words[a].size() && id < words[b].size()) { //失配的位置在a,b的中部 if(words[a][id] < words[b][id]){ //在失配位置是b的大于a的 cnt[0]++; cnt[c - words[b][id] + 1]--; cnt[c - words[a][id] + 1]++; cnt[c]--; } else{ //在失配位置是b的小于a的 cnt[c - words[a][id] + 1]++; cnt[c - words[b][id] + 1]--; } } else if(id == words[a].size() && id != words[b].size()) { //a比b短 cnt[0]++; //a 123 cnt[c]++; //b 12345 } else if(id != words[a].size() && id == words[b].size()) ; //a比b长 //a 12345 //b 123 else{ //a和b完全一样 cnt[0]++; cnt[c]--; } } int main(){ scanf("%d%d", &n, &c); rep(i, 1, n){ int len, w; scanf("%d", &len); while(len--){ scanf("%d", &w); words[i].push_back(w); } } rep(i, 1, n - 1)cal(i, i + 1); bool ok = false; int sum = 0; rep(i, 0, c - 1){ sum += cnt[i]; if(sum == n - 1){ ok = true; printf("%d\n", i); break; } } if(!ok)printf("-1\n"); return 0; }
相关文章推荐
- codeforces 731 D 80-th Level Archeology (差分+线段扫描)
- CodeForces - 731D 80-th Level Archeology
- 【codeforces 731D】80-th Level Archeology
- 【codeforces 274B】【树形DP】 B. Zero Tree【一棵树,每个点有权值,每次操作可以对一个联通子集中的点全部加或者减1,且每次操作必须包含点1,问最少多少次操作权值全为0】
- CodeForces 731D 80-th Level Archeology
- CodeForces 731 D.80-th Level Archeology(水~)
- 第五题:n 只奶牛坐在一排,每个奶牛拥有 ai 个苹果,现在你要在它们之间转移苹果,使得最后所有奶牛拥有的苹果数都相同,每一次,你只能从一只奶牛身上拿走恰好两个苹果到另一个奶牛上,问最少需要移动多少次可以平分苹果,如果方案不存在输出 -1
- 【codeforces 691 D】【并查集 或者 dfs】aps in Permutation【给一个1到N的排列,M个操作,每次可以交换X Y位置上的数字,求可以得到的最大字典序的数列】
- 【codeforces 691 E】【矩阵快速幂 思维题】【给定序列,从序列中选择k个数(可以重复选择),使得得到的排列满足xi与xi+1异或的二进制表示中1的个数是3的倍数。问满足条件的序列个数】
- Codeforces731D 80-th Level Archeology
- Codeforces Round #376 (Div. 2) D. 80-th Level Archeology
- 假设有一颗二叉树,已知这棵树的节点上不均匀的分布了若干石头,石头数跟这棵二叉树的节点数相同,石头只可以在子节点和父节点之间进行搬运,每次只能搬运一颗石头。请问如何以最少的步骤将石头搬运均匀,使得每个节
- Codeforces Round #376 (Div. 2) D. 80-th Level Archeology —— 差分法 + 线段扫描法
- 【题解】Codeforces731D 80-th Level Archeology
- poj 3308 最小点权覆盖集, 见 胡驳涛论文里面有讲见图方法 二分图最小权点覆盖 我的想法是一个东西通过两个操作都可以完成,这些操作还可以完成其他的东西,每个操作有一个花费,现在要你选出花费最小的操作。这就是裸的模型
- 假设有一颗二叉树,已知这棵树的节点上不均匀的分布了若干石头,石头数跟这棵二叉树的节点数相同,石头只可以在子节点和父节点之间进行搬运,每次只能搬运一颗石头。请问如何以最少的步骤将石头搬运均匀,使得每个节
- Codeforces Round #376 (Div. 2) D. 80-th Level Archeology(乱搞)
- 区间的交并(80-th Level Archeology,cf 731D)
- Codeforces 542A. Place Your Ad Here (扫描线进阶 带权值的线段交求最大值) (线段树)
- Codeforces 383C. Propagating tree(树上hash映射+线段树懒操作)->(一种将树结点规律化的方法)