Codeforces的这场是div1+div2的,理论上更容易上分,然而我C题被卡了...还好没掉太多分.个人感觉这一次比赛的出题人水平很高,很多题目看起来毫无思路,但是最终的解法都比较容易理解,没有用到复杂算法,非常有启发性,而且代码量都不是太大。这次比赛确实可以说是“题出的好!难度适中,覆盖知识点广,题目又着切合实际的背景,解法比较自然。给出题人点赞 !”这就是传说中的有良好区分度的题吧!(虽然我当场比赛的体验非常不好,凌晨比赛+卡题)于是今天把剩下的题目都刷了一下,写了这篇题解

A. Find Square

签到题,找出正方形的左上角和右下角顶点的位置,取平均值即可。关键代码(C++):

    read(r), read(c);rep(i, r) scanf("%s", s[i] + 1);int sx, sy, ex, ey;for(int i = 1; i <= r; ++i)for(int j = 1; j <= c; ++j)if(s[i][j] == 'B') {sx = i, sy = j; goto over;}
over: ;for(int i = r; i > 0; --i)for(int j = c; j > 0; --j)if(s[i][j] == 'B') {ex = i, ey = j;goto ed;}
ed: ;printf("%d %d\n", (sx+ex)/2, (sy + ey)/2);

B. Unnatual Conditions

思维题,即脑筋急转弯题。事实上可以很容易构造出两个数相加得到100000000000这种的,打印出来即可。(亏我想了10分钟)代码(Python):

print( '5' * 2230 )
print( '4' * 2229 +'5' )

C. Rectangles

我的思路是维护一个小根堆,里面保存考虑第i个矩形时所有可能的矩形,排序方式为出现次数的高低.具体做法是先将前两个矩形和他们的交(如果存在)加入优先队列,出现次数依次为1,1,2,再考虑把之后的矩形与优先队列中的矩形的交(如果存在)加入优先队列里,考虑完第i个矩形后弹出优先队列中出现次数为i-2和更小的矩形(因为这些矩形最多出现在n-2个矩形中,绝对不可能符合题意)。看起来很暴力,但是这样已经可以了,因为每一次优先队列最多只可能比前一次多一个元素,因此每次比较的次数也就是优先队列中元素个数,最多个,而一共n 个矩形,因此.但这样通过这道题是不够的,事实上加一个非常微小的优化就可以把复杂度降到:每次往优先队列中添加元素时检查重复,如果重复了,就直接修改已经存在的那个元素的优先级.这样做可以直接将优先队列中的元素个数降至

但是遗憾的是STL的优先队列不支持遍历每个元素,pb_ds会出奇怪的编译错误(要重载小括号运算符?)(我昨天就是被这些错误卡住了,没交上正解),可以使用make_heap或者手写堆.下面的代码是我后来实在没办法暴力复制队列元素的方法(因为队列元素个数常数级,所以即使暴力复制也只会增加常数,复杂度是不变的)140ms:

#include<bits/stdc++.h>
#define rep(i, n) for( int i = 1; i <= n; ++i )
using namespace std;
const int maxn = 142674;
int n;
struct rect{ // rectangleint lx, ly, rx, ry;rect(int x=0, int y=0, int z=0, int w=0):lx(x), ly(y), rx(z), ry(w){}bool operator == (const rect& rhs) const {return lx == rhs.lx && ly == rhs.ly && rx == rhs.rx && ry == rhs.ry;}
};
inline rect intersect(rect& r1, rect& r2){return rect(max(r1.lx, r2.lx ), max(r1.ly,r2.ly) ,min(r1.rx,r2.rx ), min(r1.ry,r2.ry));
}// 矩形的交
typedef pair<rect, int> pri;
pair<rect, int> q[10000];
signed main()
{scanf("%d", &n);int front = 0, rear = 0; // [front, rear)rep(i, n){int a,b,c,d;scanf("%d%d%d%d", &a, &b, &c, &d);rect me = rect(a, b, c, d);vector<pri> tobepush; //要加入数组的元素if (i <= 2) tobepush.push_back (make_pair(me, 1));for(register int j = rear - 1; j != front - 1; --j){//考虑将现在的矩形与数组中矩形的交加入数组rect inter = intersect(me, q[j].first);if( inter.lx <= inter.rx && inter.ly <= inter.ry ){//如果这个矩形存在bool ok = 1;for(vector<pri>::iterator it2 = tobepush.begin(); it2 != tobepush.end(); ++it2)//这个是判重,关键步骤,没有了会TLEif(it2 -> first == inter) {it2 -> second = max(it2->second, q[j].second + 1); ok = 0; break;}if(ok) tobepush.push_back(make_pair(inter, q[j].second + 1));}}int f = 0;for(int g = front; g != rear; ++g)if(q[g].second >= i - 1) q[f++] = q[g]; //暴力将数组重构front = 0, rear = f;for ( auto v : tobepush ) q[rear++] = v; //加入tobepush中的元素}printf("%d %d\n", q[front].first.lx, q[front].first.ly);//q[front].first就是一个满足条件的矩形,打印它的左下角就好了return 0;
}

D. Order Book

也是一道思维题吧..主要就是观察到令结果*2的是Accept而不是Add..维护一个保存当前元素的集合,需要考虑的元素的范围,以及在范围内的数的数量cnt.添加操作直接加入集合就行了,如果在范围内就增加计数器.每出现一个Accept,就把计数器清零,把元素删掉,找到it = lower_bound和--it (即这个元素的前驱后继)就可以断言大于*it的一定是SELL,小于*(--it)的一定是BUY ,不去考虑他们.因此更新需要考虑的范围.如果删掉的元素是需要考虑范围的边界,那Accept的元素是Buy是Sell已经确定,否则不确定,结果乘以二.这样就可以基本完成了,但是如果最后一次操作之后考虑范围内还有数怎么办?题目已经保证了Buy和Sell的价格有条件,因此这些数只有cnt+1种可能(即00...0,10...0, 11...0, ..., 11...1这些可能,0和1必须连续),最后乘以(cnt+1)即可.下面是代码(C++,296ms)

#include<bits/stdc++.h>
#define rep(i,n ) for(int i = 1; i <= n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;template <typename T>
inline void read(T& x){int w = 1; T data = 0;char ch = getchar();while(!isdigit(ch) && ch != '-') ch = getchar();if(ch == '-') w = -1, ch = getchar();while(isdigit(ch))data = 10 * data + ch - '0', ch = getchar();x = data * w;
}
template <typename T>
void write(T x){if (x < 0) putchar('-'),x = ~x + 1;if (x > 9) write(x / 10);putchar( x % 10 + '0');
}
template <typename T>
inline void writeln(T x){write(x), putchar('\n');
}
#define name(x) #x
#define PRINT(x) $ printf("%s = ", name(x)), cout << x << '\n'
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
//上面全是模板constexpr ll mod = 1000000007LL, inf = 0x3fffffffffLL, minf = -inf;
ll ans = 1LL;
set<ll> s;
signed main()
{ll q, cnt = 1, lb = minf, ub = inf; read(q);while( q-- ){char opt[20]; ll p;scanf("%s", opt); read(p);if( opt[1] == 'D' ){s.insert(p);if( p >= lb && p <= ub ) cnt++;}else{if( p < lb || p > ub ) {puts("0"); return 0;}if( p != lb && p != ub ) ans = (ans << 1LL) % mod;s.erase(p);cnt = 1;auto it = s.lower_bound(p);if( it != s.end() ) ub = *it; else ub = inf;if( it != s.begin() ) lb = *(--it); else lb = -inf;}}writeln( ans * cnt % mod );return 0;
}

E. Restore Array

是一道构造题,但是坑点很多,我wa了3次re两次。。。可以证明如果序列元素全部相同且不为0,则一定无解,反之也成立。我们有以下的构造方法:先找一个起始位置pos。方法是这样的:找到序列的最大值,查找这个值出现的所有区域,有些区域最大值是连着出现的,我们称为二连块,而我们要找最右边的二连块,并取这个二连块中位于最左边的这个值。至于为什么取这个值,读者可以自己找找反例(我当时wa了几次)例如序列1,5,4,1,5,5,5,3,5,5,2中最大值是5,三条下划线划出了三个二连块,我们要取的5就是粗体倾斜的那一个。我们从起始位置开始向左走,维护途经的数的和,最后走回来(因为是环形)得到一个新序列。例如这个例子中,先走到3,产生3+5=8,再走到5,产生5+8=13,再走到5,产生5+13=18,以此类推。这个序列与标准答案已经很接近了,可以证明它可以满足大多数性质。例如上面的序列会产生34,33,28,24,23,18,13,8,5,39,是正确的。但是还是可以把它卡掉,例如0,0,0,0,1会产生1,1,1,1,1,但是,怎么办呢?实际上很简单,走第一步时多加一倍原数就好了,例如第一个例子中走到3时本应该产生3+5=8,但是这回我们让它变成3+5+5=13,其它的数按照原来的方法不变。这样后面的例子中产生的数列是2,2,2,2,1,符合要求。可以证明这种方法是正确的。代码:(C++,109ms)

#include<bits/stdc++.h>
using namespace std;
const int maxn = 150000;
long long a[maxn], b[maxn], n, mn = 0x3f3f3f3f, mx = 1, pos;
inline int pre(int x) {return x - 1 + ((x==1) ? n : 0);} //每个位置在环上的前驱
inline int nxt(int x) {return x + 1 - ((x==n) ? n : 0);} //每个位置在环上的后继
int main(){scanf("%lld", &n);bool allzero = 1;for(int i = 1; i <= n; ++i){scanf("%lld", a + i);if( a[i] >= mx && a[i] != a[pre(i)] )mx = a[i], pos = i;if( a[i] < mn ) mn = a[i];if( a[i] != 0 ) allzero = 0;}if( mn == mx || allzero ) { //数全都一样的情况if( allzero ) {puts("YES");for(int i = 1; i <= n; ++i)printf("1%c", i == n ? '\n' : ' ');}else puts("NO");return 0;}b[pos] = a[pos];int pp = pre(pos);b[pp] = a[pp] + b[pos] * 2; //第一个走到的数多加一次原数for(int i = pre(pp); i != pos; i = pre(i) )b[i] = a[i] + b[nxt(i)]; //维护途径的数的总和puts("YES");for(int i = 1; i <= n; ++i)printf("%lld%c", b[i], i==n ? '\n' : ' ');return 0;
}

F. Make Symmetrical

这道题脑洞很大,思路新奇,我非常喜欢。首先不难想到O(n^2)的做法,但是对于10^5太大了。最核心的观察是任何一个点经过对称变换之后和原来的点必定在同一个以原点为圆心的圆上,而每个点的坐标都是整数,每个圆上的整点个数实际上很少。事实上有如下定理:(对这个定理很感兴趣的同学强烈推荐3blue1brown的数学视频:隐藏在素数规律中的π,以及配套模板题BZOJ1041: [HAOI2008]圆上的整点)

圆上整点数量定理

因此我们只需要记录当前点的个数tot,维护每个半径对应的点组成的集合circ[r],以及对于某一斜率表示已配对的点的个数组成的映射ans。新加入点的时候,tot+=1,ans中这个点与原点所成的斜率的键值+=1,这个点与set中所有同半径的点都可以配对,对应的斜率为他们中点与原点所成的斜率,对这个斜率,令ans[k]+=2.最后将这个点加入circ[r]。删除点的时候恰恰相反就可以了。查询时只需要输出tot-ans[k]。代码如下:(3182ms,估计用pbds的gp_hash_table会好得多)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
map< ll, set<pii> > circ;
map<pii, ll> ans;
ll tot;
ll gcd( ll x, ll y ) {return y== 0 ? x :  gcd(y, x % y);}
inline pii elim( ll x, ll y ) { //细节:用pair<ll,ll>保存斜率ll gc = gcd( x , y );return make_pair( x / gc, y / gc);
}
inline ll d( ll x, ll y ) {return x * x + y * y;}
int main(){int q; scanf("%d", &q);while( q-- ){int opt, x, y;scanf("%d%d%d", &opt, &x, &y);if( opt == 1 ){tot++;ans[elim( x, y )] += 1;for ( auto v : circ[d(x, y)] )ans[elim( v.first + x, v.second + y )] += 2;circ[d(x, y)].insert({x, y});} else if ( opt == 2 ){tot--;ans[elim( x, y )] -= 1;circ[d( x, y )]. erase({x, y});for( auto v : circ[d( x, y )])ans[elim(v.first + x, v.second + y)] -= 2;} else{printf("%lld\n", tot - ans[elim(x, y)]);}}
}

G. Guess the Number

H. Make Square

[Codeforces] AIM Tech Round 5 (rated, Div. 1 + Div. 2) 总结+题解相关推荐

  1. CF AIM Tech Round 5 (rated, Div. 1 + Div. 2) B. Unnatural Conditions 思维 ʕ •ᴥ•ʔ

    Let s(x)s(x) be sum of digits in decimal representation of positive integer xx. Given two integers n ...

  2. AIM Tech Round (Div. 1) C. Electric Charges 二分

    C. Electric Charges 题目连接: http://www.codeforces.com/contest/623/problem/C Description Programmer Sas ...

  3. AIM Tech Round 4 (Div. 2)ABCD

    这一场真的是血崩,a,b都被hack,还好结束前重交都过了 A:题意:找出得到k个不同的字符,所要更改的最小字符数 题解:首先如果k>字符串长度,直接impossible,然后直接记录一下不重复 ...

  4. AIM Tech Round 3 (Div. 2) A B C D

    虽然打的时候是深夜但是状态比较好 但还是犯了好多错误..加分场愣是打成了降分场 ABC都比较水 一会敲完去看D 很快的就想出了求0和1个数的办法 然后一直wa在第四组..快结束的时候B因为低级错误被h ...

  5. AIM Tech Round 4 (Div. 2)

    A题 分析:暴力 1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" ...

  6. AIM Tech Round 5C. Rectangles 思维

    C. Rectangles time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  7. Educational Codeforces Round 114 (Rated for Div. 2) (A ~ F)全题解

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 Educational Codeforces Round 114 (Rated for Div. 2) ...

  8. Educational Codeforces Round 106 (Rated for Div. 2)(A ~ E)题解(每日训练 Day.16 )

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 Educational Codeforces Round 106 (Rated for Div. ...

  9. Educational Codeforces Round 37 (Rated for Div. 2) 1

    Educational Codeforces Round 37 (Rated for Div. 2) A.Water The Garden 题意:Max想给花园浇水.花园可被视为长度为n的花园床,花园 ...

  10. Educational Codeforces Round 90 (Rated for Div. 2)(A, B, C, D, E)

    Educational Codeforces Round 90 (Rated for Div. 2) Donut Shops 思路 分三种情况: a==c/ba == c / ba==c/b这个时候两 ...

最新文章

  1. LeetCode 316. Remove Duplicate Letters--贪心--Java,C++,Python解法
  2. 运动检测(前景检测)之(二)混合高斯模型GMM
  3. boost::safe_numerics::safe_signed_range相关的测试程序
  4. mybatis 做 insert操作的时候返回插入的那条数据的id
  5. Bloom是REST API缓存中间件,充当负载平衡器Nginx和REST API服务之间的反向代理
  6. Scala基本类型及操作、程序控制结构
  7. 基于netty实现gps jtt808协议接入
  8. 人工智能对数据分析师的影响
  9. c语言英文版孤独怎么说,孤独的伤感的英文句子
  10. spack file hierarchy system
  11. PYTHON-模块 re subprocess
  12. JVM(5)_方法区和大厂面试题
  13. 搭建nlp_server服务器
  14. JS元素的提取,删除 ,添加,修改
  15. 利用Python实现矢量逐个图斑裁剪栅格,形成图斑对应的栅格文件
  16. 博客园android,博客园android客户端
  17. 实现MASW分析以及速度反演的工具包——MASWaves
  18. [校招-春招]-字节跳动-客户端开发工程师-一面-总结
  19. 奇虎360回归A股要如何上市?多家上市公司否认360借壳
  20. 域名301重定向(针对apache的.htaccess)的几种方法

热门文章

  1. QT实例-数据库分页查询
  2. 中南大学湘雅医院冯嵩:业财融合一体化信息平台的建设
  3. 用u盘进不了pe计算机意外地,u盘装系统启动不了无法进入pe怎么办
  4. eth的geth钱包安装
  5. mac 运行android模拟器速度慢,Mac下顺畅的安卓模拟器:网易MuMu
  6. 未备案域名可以用吗?域名备案有什么好处?
  7. 代理模式与委托模式的异同点
  8. java.sql.SQLException: Access denied for user ‘root‘@‘127.0.0.1‘ (using password: YES)
  9. 蛋白质中二硫键特征的质谱分析技术及其应用
  10. 计算机科学期刊重复率要求,计算机科学与应用期刊