Problem A  B-number

  题目大意 求1到n中包含连续子串"13"的数且能被13整除的数有多少个。

  ->题目传送站[here]

  不难想到数位dp,现在开始设计状态。包含子串"13"可以用0, 1, 2来标记,如果从高位到低位dp的话,意义见下表。

Status Meaning
0 子串"13"从未出现过
1 子串"13"从未出现过,但字符'1'出现在已经确定的部分的最右侧
2 子串"13"已经出现过了

  对于能被13整除的数也很好处理,直接加一个状态记录此数除以13的余数。

  然后数位dp两种实现方法:1.先用一次dp预处理出一个数组,然后根据某些套路进行计算。2.每次用记忆化搜索去做。

  方法1我在比赛的时候没写出来,下来没调出来,直接重写,用方法2。值得注意一点就是使用dp值和记录dp值的前提是没有卡着"上界"。

Code

 /**
* hdu 3652 - A
* Accepted
* author:yyf
* Time:16ms
* Memory:1876k
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - ); int n;
int digit[];
int power[];
char buf[];
int f[][][];
/**
*@param status 1 - "13" haven't appeared yet, 2 - "13" haven't appeared yet, but digit '1' at the right of the number has been OK, 3 - "13" has appeared.
*/
int dfs(int pos, int rest, int status, boolean betop) { //Digits dp
if(pos == -)
return (rest == && status == ) ? () : ();
if(!betop && f[pos][rest][status] != -)
return f[pos][rest][status]; int ret = ;
int nstatus;
boolean nbetop;
int lim = (betop) ? (digit[pos]) : ();
for(int i = ; i <= lim; i++) {
nstatus = ;
if(status == )
nstatus = ;
if(status == )
nstatus = (i == ) ? () : ();
if(nstatus == )
nstatus = (i == ) ? () : ();
ret += dfs(pos - , (rest * + i) % , nstatus, betop && i == lim);
}
if(!betop)
f[pos][rest][status] = ret; return ret;
} inline void init() {
memset(f, -, sizeof(f));
power[] = ;
for(int i = ; i < ; i++)
power[i] = power[i - ] * ;
} inline void solve() {
while(~scanf("%d", &n)) {
// memset(f, -1, sizeof(f));
int x = n, len = ;
while(x) digit[len++] = x % , x /= ;
// reverse(digit, digit + len);
printf("%d\n", dfs(len - , , , true));
}
} int main() {
init();
solve();
return ;
}

Problem A

Problem B The Captain

  题目大意 定义直角坐标系内任意两点间行走的费用是这两点横纵坐标之差的绝对值的最小值。求点1到点n的最小费用。

  ->题目传送站[here]

  我开始以为是dp加什么鬼畜的堆优化,优化到O(nlogn),然而答案是图论大水题。。。

  考虑3个点,设它们的坐标分别为,设两点X, Y间的最小费用为f(X, Y),则不难得到。对于当y满足这个关系时,也可以得到相似的结论。同时这个也可以推广到点数更多的时候。

  因此建图时只需按横纵坐标排序,将相邻的点连边。不过据说要卡不加任何优化的spfa。。(虽然贴的是队友的代码,感觉不太好,但还是这么愉快地贴了)

Code

 /**
* bzoj 4152 - B
* Accepted
* Author:lemonoil
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
using namespace std;
const int N = ;
const int inf = ;
int n,k;
int xx[N],yy[N],d[N],tot,head[N];
struct edge{
int to,w,next;
}e[N<<];
struct data{
int x,y;
void read(){
scanf("%d%d",&x,&y);
}
}a[N<<];
int cmpx(const int i,const int j){
return a[i].x<a[j].x;
}
int cmpy(const int i,const int j){
return a[i].y<a[j].y;
}
void addege(int x,int y,int z){
e[++tot].next=head[x];e[tot].to=y;e[tot].w=z;head[x]=tot;
}
void add(int x,int y,int z){
addege(x,y,z);addege(y,x,z);
}
struct node{
int x,d;
bool operator < (const node & rhs)const{
return d>rhs.d;
}
node(){}
node(int x,int d):x(x),d(d){}
};
priority_queue<node> Q;
void dijk(){
for(int i=;i<=n;i++)d[i]=inf;
Q.push(node(,));d[]=;
while(!Q.empty()){
node t=Q.top();Q.pop();
if(d[t.x]!=t.d)continue;
for(int i=head[t.x];i;i=e[i].next){
if(d[e[i].to]>d[t.x]+e[i].w){
d[e[i].to]=d[t.x]+e[i].w;
Q.push(node(e[i].to,d[e[i].to]));
}
}
}
}
int main(){
scanf("%d",&n);
for(int i=;i<n;i++){
a[i].read();
xx[i]=yy[i]=i;
}
sort(xx,xx+n,cmpx);
for(int i=;i<n-;i++){
data *x=a+xx[i],*y=a+xx[i+];
if((y->x-x->x)<=abs(x->y-y->y)){
add(xx[i],xx[i+],(y->x - x->x));
}
}
sort(yy,yy+n,cmpy);
for(int i=;i<n-;i++){
data *x=a+yy[i],*y=a+yy[i+];
if((y->y-x->y)<=abs(x->x-y->x)){
add(yy[i],yy[i+],y->y - x->y);
}
}
dijk();
printf("%d\n",d[n-]);
return ;
}

Problem B

Problem C 聪明的猴子

  题目大意 在森林里有m只猴子和n棵树,给出每棵树的坐标和每只猴子的最远跳跃距离,问有多少只猴子能够直接或间接到达所有树。

  ->题目传送站[here]

  最小生成树,不解释这些。(队友又抢我写代码的机会。。还好1a)

 /**
* bzoj 2429 - C
* Accepted
* Author:Maverick
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list> using namespace std; const int maxn = ;
const int maxm = ;
int n,m,tot,fin,ans;
int jp[maxm],x[maxn],y[maxn];
struct Edge {
int frm,to;
double len;
bool operator < (const Edge & a) const {
if(len != a.len) return len < a.len;
if(frm != a.frm) return frm < a.frm;
return to < a.to;
}
} e[maxn*maxn];
int fa[maxn*maxn]; void addedge(int frm, int to) {
e[++tot].frm = frm;
e[tot].to = to;
int len = (x[frm]-x[to])*(x[frm]-x[to])+(y[frm]-y[to])*(y[frm]-y[to]);
e[tot].len = sqrt(len);
} int find(int frm) {
if(frm == fa[frm]) return frm;
return fa[frm] = find(fa[frm]);
} void unionn(int frm, int to) {
if(find(frm) != find(to)) fa[find(to)] = find(frm);
} int main () {
scanf("%d",&m);
for(int i = ; i <= m; i++) scanf("%d",&jp[i]);
scanf("%d",&n);
for(int i = ; i <= n; i++) scanf("%d%d",&x[i],&y[i]);
for(int i = ; i <= n; i++)
for(int j = ; j <= n; j++)
if(i != j) addedge(i, j);
sort(e+, e++tot);
for(int i = ; i <= n*n; i++) fa[i] = i;
for(int i = ; i <= tot; i++) {
if(find(e[i].to) != find(e[i].frm)) unionn(e[i].to, e[i].frm), fin++;
if(fin == n-) { ans = e[i].len; break; }
}
int cnt = ;
for(int i = ; i <= m ; i++) if(jp[i] >= ans) cnt++;
printf("%d\n",cnt);
return ;
}

Problem C

Problem D 互不侵犯King

  题目大意 国王会攻击它周围的8个方框内的其他棋子,求在一个n * n的棋盘内放k个国王使得它们不互相攻击的方案数。

  ->题目传送站[here]

  由于n很小,很容易想到状压dp(其实我队友想爆搜),我开始试图用轮廓线,毕竟棋盘形,设计了状态,发现会T掉,重新设计了状态,应该可以AC,不想写直接扔给队友(我怀疑我是来坑队友的),队友很强用朴素状压dp把它水掉。

  思路是用f[i][s][k]表示第i行,放国王的位子(有国王的位子为1,没有国王的位子为0),已经选了k个国王的方案数,转移时暴力枚举旧状态和新状态,再判断是否合法。但是这样会TLE,所以队友提前暴力预处理转移表,合法为true,不合法为false,就可以实现O(1)转移了。主dp过程就四层for大暴力就好了。

Code

 /**
* bzoj 1087 - D
* Accepted
* author:lemonoil
*/
#include<iostream>
#include<cstdio>
using namespace std;
int n,m,tot,sum[];
long long f[][][];
int p[],g[][];
long long ans,s;
inline void read(int &res){
static char ch;int flag=;
while((ch=getchar())<''||ch>'')if(ch=='-')flag=-;res=ch-;
while((ch=getchar())>=''&&ch<='')res=res*+ch-;res*=flag;
}
void init(){
for(register int i=;i<=tot;i++)
if((i&(i>>))==){s=;
for(register int x=i;x;x>>=)s+=(x&);
sum[i]=s,p[i]=;
}
for(register int i=;i<=tot;i++)if(p[i])
for(register int j=;j<=tot;j++)if(p[j])
if(((i&j)==)&&((i&(j>>))==)&&((j&(i>>))==))g[i][j]=;
}
int main(){
read(n),read(m);
tot=(<<n)-;init();
for(register int i=;i<=tot;i++)if(p[i])f[][sum[i]][i]=;
for(register int j=;j<n;j++)
for(register int k=;k<=tot;k++)if(p[k])
for(register int i=;i<=tot;i++)if(p[i])if(g[k][i])
for(register int p=sum[k];p+sum[i]<=m;p++)
f[j+][p+sum[i]][i]+=f[j][p][k];
for(register int i=;i<=tot;i++)ans+=f[n][m][i];
printf("%lld\n",ans);
return ;
}

Problem D

Problem E 管道取珠

  (不会概述题目,请自行戳这里查看原题)

  看到这道题,我一直在想组合数乱搞。。后来发现正确理解a^2的意义后,变成了一道dp水题,但仍然全场无队AC.。。

  大神们将平方理解成两个进行相同的游戏,最后得到相同的序列的方案数。于是用f[i][j][k]表示总共取了i个球,第一个人在上面取了j个球,第二个人在下面取了k个球。转移是显然的,不清楚可以看代码。

Code

 /**
* bzoj 1566 - E
* Accepted
* author:yyf
* Time:9292ms
* Memory:3280k
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - ); const int moder = ; inline int mod_plus(int a, int b) {
int ret = a + b;
while(ret >= moder) ret -= moder;
return ret;
} #define smod_plus(a, b) a = mod_plus(a, b) int n, m;
char a[];
char b[];
int f[][][]; inline void init() {
scanf("%d%d%s%s", &n, &m, a, b);
} inline void solve() {
int t = ;
f[t][][] = ;
for(int i = ; i < n + m; i++) {
t ^= ;
memset(f[t], , sizeof(f[t]));
for(int j = ; j <= n && j <= i; j++) {
for(int k = ; k <= n && k <= i; k++) {
int x = i - j, y = i - k;
if(j < n && k < n && a[j] == a[k])
smod_plus(f[t][j + ][k + ], f[t ^ ][j][k]);
if(x < m && k < n && b[x] == a[k])
smod_plus(f[t][j][k + ], f[t ^ ][j][k]);
if(j < n && y < m && a[j] == b[y])
smod_plus(f[t][j + ][k], f[t ^ ][j][k]);
if(x < m && y < m && b[x] == b[y])
smod_plus(f[t][j][k], f[t ^ ][j][k]);
}
}
}
printf("%d\n", f[t][n][n]);
} int main() {
init();
solve();
return ;
}

Problem E

Problem F 树上操作

  (题目太裸,请自行戳这里查看原题)

  树链剖分不解释,如果不会可以看这篇博客[here]

  由于比赛时怕写得很长(大概也就200行左右吧。。),半天调不出来,果断让给队友。

Code

 /**
* bzoj 4034 - F
* Accepted
* author:lemonoil
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
using namespace std;
typedef bool boolean;
typedef long long ll;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b)
#define R(rt) rt<<1|1
#define L(rt) rt<<1
const int N=;
using namespace std;
int tot,n,m,next[N],point[N],v[N],deep[N],fa[N],size[N],son[N];
ll tr[N*],delta[N*];
int val[N],l[N],r[N],q[N],pos[N],cnt,belong[N];
inline void add(int x,int y){
next[++tot]=point[x]; point[x]=tot; v[tot]=y;
next[++tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline void dfsf(int x,int f){
deep[x]=deep[f]+;
size[x]=;
for(register int i=point[x];i;i=next[i])
if(v[i]!=f){
fa[v[i]]=x;
dfsf(v[i],x);
size[x]+=size[v[i]];
if(size[son[x]]<size[v[i]])son[x]=v[i];
}
}
inline void dfsfs(int x,int chain){
belong[x]=chain; pos[x]=++cnt; l[x]=r[x]=cnt; q[cnt]=x;
if(!son[x])return;
dfsfs(son[x],chain);
for(register int i=point[x];i;i=next[i])
if(v[i]!=fa[x]&&v[i]!=son[x])
dfsfs(v[i],v[i]);
r[x]=cnt;
}
inline void update(int rt){
tr[rt]=tr[L(rt)]+tr[R(rt)];
}
inline void build(int rt,int l,int r){
if(l==r){
tr[rt]=(ll)val[q[l]];
return;
}
register int mid=(l+r)>>;
build(L(rt),l,mid);
build(R(rt),mid+,r);
update(rt);
}
inline void pushdown(int rt,int l,int r){
register int mid=(l+r)>>;
if(delta[rt]){
tr[L(rt)]+=(ll)(mid-l+)*delta[rt];
tr[R(rt)]+=(ll)(r-mid)*delta[rt];
delta[L(rt)]+=delta[rt];
delta[R(rt)]+=delta[rt];
delta[rt]=;
}
}
inline void update_point(int rt,int l,int r,int x,int v){
if(l==r){
tr[rt]+=(ll)v;
return;
}
register int mid=(l+r)>>;
pushdown(rt,l,r);
if(x<=mid)update_point(L(rt),l,mid,x,v);
else update_point(R(rt),mid+,r,x,v);
update(rt);
}
inline void change(int rt,int l,int r,int ll,int rr,int v){
if(ll<=l&&r<=rr){
tr[rt]+=(long long)v*(long long)(r-l+);
delta[rt]+=(long long)v;
return;
}
register int mid=(l+r)>>;
pushdown(rt,l,r);
if(ll<=mid)change(L(rt),l,mid,ll,rr,v);
if(rr>mid)change(R(rt),mid+,r,ll,rr,v);
update(rt);
}
inline long long query_sum(int rt,int l,int r,int ll,int rr){
if(ll<=l&&r<=rr)return tr[rt];
int mid=(l+r)/;
register long long ans=;
pushdown(rt,l,r);
if(ll<=mid)ans+=query_sum(L(rt),l,mid,ll,rr);
if(rr>mid)ans+=query_sum(R(rt),mid+,r,ll,rr);
return ans;
}
inline long long solve(int x,int y){
ll ans=;
while(belong[x]!=belong[y]){
if(deep[belong[x]]<deep[belong[y]])swap(x,y);
ans+=query_sum(,,n,pos[belong[x]],pos[x]);
x=fa[belong[x]];
}
if(deep[x]>deep[y])swap(x,y);
ans+=query_sum(,,n,pos[x],pos[y]);
return ans;
}
inline void read(int &res){
static char ch;int flag=;
while((ch=getchar())<''||ch>'')if(ch=='-')flag=-;res=ch-;
while((ch=getchar())>=''&&ch<='')res=res*+ch-;res*=flag;
}
int main(){
read(n),read(m);
for(register int i=;i<=n;i++)read(val[i]);
for(register int i=;i<n;i++){
register int x,y; read(x),read(y);
add(x,y);
}
dfsf(,); dfsfs(,);
build(,,n);
for(register int i=;i<=m;i++){
int opt,x,y;read(opt);
if(opt==){
read(x),read(y);
update_point(,,n,pos[x],y);
}if(opt==){
read(x),read(y);
change(,,n,l[x],r[x],y);
}if(opt==){
read(x);
printf("%I64d\n",solve(,x));
}
}
}

Problem F

Problem G 覆盖问题

  题目大意 给定平面上n个点,求用3个完全相同正方形邻边分明平行x轴和y轴覆盖n个点(包括边界),的最小边长。

  黄学长O(n)贪心膜拜一发。

  现在开始扯您能听懂的方法。

  不难想到二分答案。关键在于check。考虑用一个巨大的矩形刚好框住这些点(如果不清楚就看看下图)。

  

  因为每条边上都有1个点,因此可以得出结论至少有一个矩形覆盖了这个矩形的一个顶点(当矩形数变成4时不一定成立)。所以可以枚举用正方形覆盖哪一个角落然后打上标记,递归处理,再判下边界判断即可。还是比较好写。

Code

 /**
* bzoj 1052 - G
* Accepted
* Author:yyf
* Time:268ms
* Memory:1448k
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - );
const double eps = 1e-; typedef class Point {
public:
int x;
int y;
Point(int x = , int y = ):x(x), y(y) { }
}Point; int n;
Point *ps; inline void init() {
scanf("%d", &n);
ps = new Point[n];
for(int i = ; i < n; i++)
scanf("%d%d", &ps[i].x, &ps[i].y);
} inline boolean inside(Point p, Point r, int wid, int hei) {
return p.x >= r.x && p.x <= (r.x + wid) && p.y >= r.y && p.y <= (r.y + hei);
} int cover;
int *cov;
boolean dfs(int dep, int L) {
int maxx = , maxy = , minx = inf, miny = inf;
for(int i = ; i < n; i++) {
if(cov[i] != -) continue;
smax(maxx, ps[i].x);
smin(minx, ps[i].x);
smax(maxy, ps[i].y);
smin(miny, ps[i].y);
} if(dep == ) {
return maxx - minx <= L && maxy - miny <= L;
} Point br[] = {Point(minx, miny), Point(minx, maxy - L), Point(maxx - L, miny), Point(maxx - L, maxy - L)};
for(int k = ; k < ; k++) {
int cnt = ;
for(int i = ; i < n; i++)
if(cov[i] == - && inside(ps[i], br[k], L, L))
cov[i] = dep, cnt++;
cover += cnt;
if(dfs(dep + , L)) return true;
cover -= cnt;
for(int i = ; i < n; i++)
if(cov[i] == dep)
cov[i] = -;
}
return false;
} boolean check(int mid) {
cover = ;
memset(cov, -, sizeof(int) * n);
return dfs(, mid);
} inline void solve() {
cov = new int[n];
int l = , r = 1e8;
while(l <= r) {
int mid = (int)((l * 1LL + r) >> );
if(check(mid)) r = mid - ;
else l = mid + ;
}
printf("%d\n", r + );
} int main() {
init();
solve();
return ;
}

Problem G

Problem H 楼房重建

  题目大意 平面上有些线段,一段在y轴且平行于x,另一端在第一象限。有一些修改操作,修改线段的长度,每次修改完询问平面内有多少条线段再第一象限内的端点和原点的连线不与其他线段相交(包括端点)。

  ->题目传送站[here]

  我开始看这道题的时候想到了分块,然后队友立刻否决我的想法。然后比赛结束后,光速被打脸了。。

  首先简化一下问题吧。不与其他线段相交的条件是在第一象限内的端点与原点连线的斜率严格大于之前的连线的斜率。求的是"连续"严格上升子序列(请区别于LIS)。为了更好说明所说的这个序列指何物:

  原序列 1 4 2 7 3 9 4 5 6

  LIS可以是:   1 2 4 5 6

  但我所说的序列是:1 4 7 9

  先说分块做法吧,按照常规,每块大小个元素,记录对应位置的连线的斜率和当前块的这样的上升序列。修改操作直接暴力重构整个块的序列。时间复杂度。对于查询操作就从第一个块开始遍历,记录当前找到的最大的斜率,在下一个中进行upper_bound(二分快速找到合法的第一个),更新答案,时间复杂度$O(\sqrt{n} \log n)$。所以总时间复杂度$O(n\sqrt{n} \log n)$,还是可以卡过去的(毕竟时限这么宽松嘛)。

Code

 /**
* bzoj 2957 - H
* Accepted
* author:yyf
* Time:5528ms
* Memory:3312k
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - );
const double eps = 1e-; typedef class Chunk { // Blocks
public:
int size; // The size of the chunk.
int slis;
double arr[];
double lis[]; Chunk():size(), slis() { }
Chunk(int s):size(s), slis() { } void rebuild() {
slis = ;
double temp = ;
for(int i = ; i < size; i++)
if(arr[i] > temp + eps)
temp = arr[i], lis[slis++] = temp;
} int upper_bound(double x) {
return std::upper_bound(lis, lis + slis, x) - lis;
}
}Chunk; int n, m;
int csize;
int cc = ;
Chunk* cs; inline void init() {
scanf("%d%d", &n, &m);
csize = (int)sqrt(n + 0.5);
cs = new Chunk[csize + ];
for(; cc * csize <= n; cc++) {
cs[cc].size = csize;
for(int i = ; i < csize; i++) {
cs[cc].arr[i] = ;
}
}
} inline void solve() {
int pos, h;
while(m--) {
scanf("%d%d", &pos, &h);
int id = pos / csize;
cs[id].arr[pos % csize] = h * 1.0 / pos;
cs[id].rebuild();
double temp = ;
int res = ;
for(int i = ; i < cc; i++) {
int p = cs[i].upper_bound(temp);
if(p < cs[i].slis) {
res += cs[i].slis - p;
temp = cs[i].lis[cs[i].slis - ];
}
}
printf("%d\n", res);
}
} int main() {
init();
solve();
return ;
}

Problem H (Block Dividing)

  还有一种做法是用线段树。

  不用废话的是需要记录在当前一段内斜率递增的序列的长度。为了更好地维护还需要记录最大的斜率。

  维护时当区间的最大斜率小于等于要求的最小斜率时直接记0。

  如果左边的最大息率小于等于要求的最小斜率时,对答案的贡献为0,用这个方法递归处理右边。

  否则右边是可以计算出来的,因为右边看得到的建筑还是看得到,看不到的,还是看不到,因此就是val - l->val。所以用这个方法递归处理左边。

  然后判判边界就好了。

  因为每次递归的子问题大小都为原问题的一半,所以维护的时间复杂度为$O(\log n)$,因此总时间复杂度为$O(n\log^2 n)$.

  最后想吐槽一下这道题,建筑队脑子可能是有问题吧,还要不要别人住房子啊?

Code

 /**
* bzoj 2957 - H
* Accepted
* author:yyf
* Time:1484ms
* Memory:9108k
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - );
const double eps = 1e-; typedef class SegTreeNode {
public:
SegTreeNode *l, *r;
int len;
double val; SegTreeNode():l(NULL), r(NULL), len(), val(0.0) { } int count(double low) {
if(!l) return (val > low) ? () : ();
if(val <= low) return ;
if(l->val <= low) return r->count(low);
return l->count(low) + len - l->len;
} void pushUp() {
val = max(l->val, r->val);
len = l->len + r->count(l->val);
}
}SegTreeNode; typedef class SegTree {
public:
SegTreeNode* pool;
SegTreeNode* tail; SegTreeNode* root; SegTree():root(NULL) { }
SegTree(int n) {
pool = new SegTreeNode[(n << )];
tail = pool;
build(root, , n);
} SegTreeNode* newnode() {
return tail++;
} void build(SegTreeNode*& node, int l, int r) {
node = newnode();
if(l == r) return;
int mid = (l + r) >> ;
build(node->l, l, mid);
build(node->r, mid + , r);
} void update(SegTreeNode*& node, int l, int r, int idx, double s) {
if(l == r) {
node->val = s;
node->len = ;
return;
}
int mid = (l + r) >> ;
if(idx <= mid) update(node->l, l, mid, idx, s);
else update(node->r, mid + , r, idx, s);
node->pushUp();
} int query() {
return root->len;
}
}SegTree; int n, m;
SegTree st; inline void init() {
scanf("%d%d", &n, &m);
st = SegTree(n);
} inline void solve() {
int pos, h;
double slope;
while(m--) {
scanf("%d%d", &pos, &h);
slope = h * 1.0 / pos;
st.update(st.root, , n, pos, slope);
printf("%d\n", st.query());
}
} int main() {
init();
solve();
return ;
}

Problem H (Segment Tree)

Problem I 谁能赢呢?

  题目大意 在一个n * n的棋盘的最左上角有一个棋子,两个人轮流移动这个棋子,每次只能向上下左右四个方向移动一格,不能走重复的路,不能移出边界,谁先不能移动谁就输,如果两人采取最优的策略,问最后谁会赢。

  ->题目传送站[here]

  没有保持R1的传统,直接第一个A掉最水的一道题。(我们教练期望我们在15分钟内A掉这道题)。

  不说过多的废话,题解就是如果n是偶数先手必胜,否则先手必败。

  至于证明,我看到的有一种说法就是将先手和后手连续的一步看成一个1×2的多米诺骨牌。当n为偶数时,少了左上角的一块后,不存在完美覆盖,所以此时先手必胜。n为奇数时少了左上角的一块后,存在完美覆盖,所以先手必败。

Code

 /**
* bzoj 2463 - I
* Accepted
* author:yyf
*/
#include <iostream>
using namespace std; int main() {
int n;
while(cin >> n) {
if(n == ) break;
if(n & ) cout << "Bob" << endl;
else cout << "Alice" << endl;
}
return ;
}

Problem I

Problem J 仪仗队

  题目大意 求横纵坐标都为整数,且都小于n ,并且互质的点有多少个。

  ->题目传送站[here]

  由于我比较蠢,傻逼地用容斥。大佬们直接欧拉函数求phi(1) + ... + phi(n - 1)乘2再加1就是答案。。

Code

 /**
* bzoj 2190 - J
* Accepted
* author:yyf
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cmath>
#include <cctype>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <set>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <list>
#ifdef WIN32
#define Auto "%I64d"
#else
#define Auto "%lld"
#endif
using namespace std;
typedef bool boolean;
#define smax(a, b) a = max(a, b)
#define smin(a, b) a = min(a, b);
const signed int inf = (signed)((1u << ) - ); int n; inline void init() {
scanf("%d", &n);
} const int limit = 4e4;
int num = ;
int pri[];
boolean vis[];
int phi[]; inline void Euler() {
memset(vis, false, sizeof(vis));
vis[] = true, phi[] = ;
for(int i = ; i <= limit; i++) {
if(!vis[i]) pri[num++] = i, phi[i] = i - ;
for(int j = ; j < num; j++) {
int x = i * pri[j];
if(x > limit) break;
vis[x] = true;
if((i % pri[j]) == ) {
phi[x] = phi[i] * pri[j];
break;
}
phi[x] = phi[i] * (pri[j] - );
}
}
} int num1;
int fac[];
void getFactory(int a) {
num1 = ;
for(int i = ; pri[i] * pri[i] <= a; i++) {
if((a % pri[i]) == ) {
fac[num1++] = pri[i];
while((a % pri[i]) == ) a /= pri[i];
}
}
if(a != ) fac[num1++] = a;
} int getHz(int a, int moder) {
getFactory(moder);
int ret = ;
for(int i = ; i < ( << num1); i++) {
int temp = , j = , aFlag = -, x = i;
while(x) {
if(x & ) temp *= fac[j], aFlag *= -;
x >>= , j++;
}
ret += a / temp * aFlag;
}
return a - ret;
} long long res = ;
inline void solve() {
res += (n - );
int les = n - ;
for(int i = ; i < n; i++) {
// cout << res << endl;
res += getHz(les, i);
}
printf(Auto"\n", res);
} int main() {
init();
Euler();
solve();
return ;
}

Problem J

2017.7.4 ACM校内赛 Round 2的更多相关文章

  1. 2017年广东省ACM省赛(GDCPC-2017)总结

    今年第一次参加省赛(也是第一次参加这类比赛),既有点惊喜又有点紧张,前一晚没有怎么睡好,有点怕今年打铁,虽然说大一打铁也说的过去,可是谁也不想打铁. 一开始比赛的时候我一开始没有去看题,而是把我们队的 ...

  2. 2017年浙工大迎新赛热身赛 L cayun日常之赏月【易错特判】

    题目描述(https://www.nowcoder.com/acm/contest/51#question) 在cayun星球月亮大小都有一个规律,月亮为每30天一个周期,在这30天的周期里,月亮的大 ...

  3. 第六届acm省赛总结(退役贴)

    前言: 这是我的退役贴,之前发到了空间里,突然想到也要在博客里发一篇,虽然我很弱,但是要离开了还是有些感触,写出来和大家分享一下,希望不要见笑.回来看看,这里也好久没有更新了,这一年确实有些懈怠,解题 ...

  4. [原]sdut2624 Contest Print Server (大水+大坑)山东省第四届ACM省赛

    本文出自:http://blog.csdn.net/svitter 原题:http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&am ...

  5. 2014 ACM省赛总结

    今年ACM省赛已经过去一个星期左右了,2年的ACM训练是该做个总结了,因为前几日去參加蓝桥杯总决赛,所以没来的及写总结,如今在这小小总结一下吧-- 依晰记得去年省赛时候的样子,如今感觉那时像是个无知的 ...

  6. GDOI模拟赛Round 1

    GDOI模拟赛Round 1 数据结构 题目描述:给出一个长度为\(n\)的序列,支持两种操作: 1.对某段区间都加上一个数 2.给出\(p.k\),求下面表示式对\((10^9+7)\)取模 \[\ ...

  7. 第一次参加acm区域赛

    什么,这周天就要去参加acm焦作赛,简直不敢相信.从大一暑假七月份中旬到今天十一月23日,加入acm将近四个多月的时间,如今到了检验自己的时候了.aaaaaaaaaa.乌拉,必胜.打印个模板,在跑个步 ...

  8. 记:青岛理工ACM交流赛筹备工作总结篇

    这几天筹备青岛理工ACM交流赛的过程中遇到了不少问题也涨了不少经验.对非常多事也有了和曾经不一样的看法, ​一直在想事后把这几天的流水帐记一遍,一直没空直到今天考完C++才坐下来開始动笔.将这几天的忙 ...

  9. 2017杭电ACM集训队单人排位赛 - 6

    2017杭电ACM集训队单人排位赛 - 6 排名 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 59 1 X X 1 1 X X 0 1 ...

随机推荐

  1. easyui 特殊操作

    --EasyUI - datagrid中单元格里编辑控件的单击事件如何获取当前行的index var rowIndex = $(this).parents('.datagrid-row').attr( ...

  2. 【紫书】【重要】Not so Mobile UVA - 839 递归得漂亮

    题意:判断某个天平是否平衡,输入以递归方式给出. 题解:递归着输入,顺便将当前质量作为 &参数 维护一下,顺便再把是否平衡作为返回值传回去. 坑:最后一行不能多回车 附:天秀代码 #defin ...

  3. MySQL命令:创建数据库、插入数据

    简介: 学习mysql环境为ubantu,下面记录一些基本的创建数据库和插入数据的口令 打开MySQL 服务并使用 root 登录: --打开 MySQL 服务 sudo service mysql ...

  4. REQUEST FORM 实例

    https://www.programcreek.com/python/example/51524/flask.request.form

  5. 火币Huobi API

    本文介绍火币Huobi API REST API 简介 火币为用户提供了一套全新的API,可以帮用户快速接入火币PRO站及HADAX站的交易系统,实现程序化交易. 访问地址 适用站点 适用功能 适用交 ...

  6. mvc debug无法进入controller

    可能原因为,工程更改名称 进入工程bin目录下,删除所有文件即可

  7. Jquery WeUI(一)

    用于微信端的控件UI , 首先,需要做的是开发一个微信能访问的网页,并和微信关联 A. 创建一个空网站 B. 增加一般处理程序 A. 增加 web 网页 和空文件到项目中 B. 申请和配置测试服务 创 ...

  8. java-信息安全(八)-迪菲-赫尔曼(DH)密钥交换【不推荐,推荐Oakley】

    概述 信息安全基本概念: DH(Diffie–Hellman key exchange,迪菲-赫尔曼密钥交换) DH 是一种安全协议,,一种确保共享KEY安全穿越不安全网络的方法,它是OAKLEY的一 ...

  9. 11.sklearn.preprocessing.LabelEncoder的作用

    In [5]: from sklearn import preprocessing ...: le =preprocessing.LabelEncoder() ...: le.fit(["p ...

  10. gradle build scan

    1:gradle build scan 用于视图审查 构建步骤如下  https://guides.gradle.org/creating-build-scans/?_ga=2.80362963.59 ...