2019.2.26模拟
T1.电灯
题目描述不贴了。
这题好像有很多做法。题目还是很简单的……但是自己很菜还想了好久。
容易知道所获得的最长交错序列必然是由多个交错序列拼接的,所以只要每次枚举反转哪一个序列更新答案即可。
intlsy的做法是首先处理出来,如果想修改成交替序列有哪些位置需要被修改,之后取其中连续段更新答案。复杂度都是\(O(n)\)
T2.比萨
题目描述不贴了。
这题比上面做法还多。有一种\(O(n)\)的玄学做法,不会,可以问Dukelv
尺取法的做法没太懂。
我的做法是贪心+二分。一开始容易想到\(O(n^2logn)\)的暴力,之后我们只要把二分判定过程中的贪心改成用\(lowerbound\)去找那个符合条件的点在哪就行。
如果不合法继续枚举断点,然后二分找中间切的位置,具体实现使用前缀和维护。复杂度\(O(nlog^2n)\)
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
using namespace std;
typedef long long ll;
const int M = 400005;
ll read()
{
ll ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
ll n,a[M],sum[M],L,R,ans;
bool check(ll x)
{
rep(i,1,n)
{
int k = lower_bound(sum+i,sum+n+i,x+sum[i-1])-sum;
int p = lower_bound(sum+k+1,sum+n+i,x+sum[k])-sum;
if(sum[n+i-1] - sum[p] >= x) return 1;
}
return 0;
}
int main()
{
n = read();
rep(i,1,n) a[i] = a[i+n] = read();
rep(i,1,n<<1) sum[i] = sum[i-1] + a[i];
ll L = 1,R = sum[n]/3;
while(L < R)
{
ll mid = (L+R+1) >> 1;
if(check(mid)) L = mid;
else R = mid - 1;
}
printf("%lld\n",L);
return 0;
}
T3.断层
这题是JOI 2016Final的T5.传送门
自己是想不出来了。
做法非常巧妙,因为斜着修改难以维护,所以我们整个将坐标轴旋转45度。(标程是顺时针但讲题时候是逆时针)
这里说逆时针旋转。这样的话,k=1的操作相当于是给定一条竖着的直线,将其左边的点向上移,k=2的操作是给定一条横着的直线,将上面的点左移。
显然可以想到用线段树/bit去维护横纵坐标。
但是正着做是不行的,因为我们无法保证单调性。我们考虑倒推,倒着做所有的操作,改变操作方向。这时候我们发现,任意一次操作必然不会改变x,y之间的相对大小。(因为一开始每个点的坐标是(i,i),横纵坐标都递增。每次我们都是把更小的纵坐标下移,更大的横坐标右移)
这样就能保证线段树/bit维护的每一个点的下标就是按照其原来的顺序排列的,直接这样倒着推回去,把坐标变换回来输出答案即可。
变换的时候坐标从\((x,y)\)到\((x+y,x-y)\),所以回来的时候是\((\frac{x+y}{2},\frac{x-y}{2})\)
具体的实现细节有很多,首先是旋转之后的线段长度,每个都是原来的\(\sqrt{2}\)倍,而原来每次上移1个单位相当于移动了\(\sqrt{2}\)个单位,所以要把移动长度乘以2。
再者我们令每个点表示其右面的一块土地,所以坐标是从\(0~n-1\)的,而且对于涉及x的操作每个也要-1.
线段树不知道为啥跑的死慢,用树状数组的时候要注意把x+1,否则容易卡死。
每次需要用二分查找你修改的区间,复杂度\(O(nlog^2n)\)
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
const int M = 400005;
int read()
{
int ans = 0,op = 1;char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
return ans * op;
}
int n,q;
struct quake
{
int x,l,d;
}a[M];
struct tarray
{
ll c[M];
void add(int x,int val){x++;while(x < n+2) c[x] += val,x += lowbit(x);}
ll ask(int x){x++;ll cur = 0;while(x) cur += c[x],x -= lowbit(x);return cur;}
void modify(int l,int r,int val) {add(l,val),add(r+1,-val);}
}tx,ty;
int main()
{
n = read(),q = read();
rep(i,1,q)
{
a[i].x = read(),a[i].d = read(),a[i].l = read() << 1;
if(a[i].d == 1) a[i].x--;
}
rep(i,0,n) tx.modify(i,i,i),ty.modify(i,i,i);
per(i,q,1)
{
if(a[i].d == 1)
{
int L = 0,R = n-1,ans = -1;
while(L <= R)
{
int mid = (L+R) >> 1;
ll k = tx.ask(mid);
if(k <= a[i].x) ans = mid,L = mid + 1;
else R = mid - 1;
}
if(ans != -1) ty.modify(0,ans,-a[i].l);
}
if(a[i].d == 2)
{
int L = 0,R = n-1,ans = -1;
while(L <= R)
{
int mid = (L+R) >> 1;
ll k = ty.ask(mid);
if(k < a[i].x) L = mid + 1;
else ans = mid,R = mid-1;
}
if(ans != -1) tx.modify(ans,n-1,a[i].l);
}
}
rep(i,0,n-1)
{
ll kx = tx.ask(i),ky = ty.ask(i);
printf("%lld\n",(kx-ky)>>1);
}
return 0;
}