【2018.11.2】相遇 / 求和 / 小乔

这两天钻我的模拟赛钻得思想僵化了,本来打算弃考,最后半小时才决定手糊一波。

题目

题解

T1

原题……$dp$ / $bfs$ 都行(因为所有边权都是 $1$,第一次扩展到的就是最短路)。

然而我判错了向左走的情况,一下被爆了,降智。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0; bool f=1; char c=getchar();
 5     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
 6     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
 7     if(f) return x;
 8     return 0-x;
 9 }
10 int a,b,dp[200002];
11 int main(){
12     freopen("meet.in","r",stdin);
13     freopen("meet.out","w",stdout);
14     a=read(),b=read();
15     memset(dp,0x7f,sizeof dp);
16     dp[a]=0;
17     for(int i=a-1;i>=0;--i) dp[i]=dp[i+1]+1;
18     for(int i=0;i<=b;++i){
19         dp[i+1]=min(dp[i+1],dp[i]+1);
20         if((i<<1)-1<=b) dp[i<<1]=min(dp[i<<1],dp[i]+1), dp[(i<<1)-1]=min(dp[(i<<1)-1],dp[i]+2);
21     }
22     printf("%d\n",dp[b]);
23     return 0;
24 }
View Code

 

T2

我之前做过类似的题,然后现在又忘了-_- 再次降智

题解 $pdf$ 里面貌似写的很清楚了……

必定先考虑全是 $1$ 的情况。打个暴力,然后仔细观察矩阵中每个数是怎么转移来的,发现就是它上面和左面的数相加得到。

那这不就是个斜的杨辉三角么?

如图,最右边一行(橙框部分)是初始序列,然后每操作一次,序列就变为左下一行。

这是初始序列全是 $1$ 的情况,那对于任意初始序列的情况呢?

我们发现,最终序列的每个数,是由初始序列中每个数 乘上很多次得来的。

那系数是多少?

假设杨辉三角从第 $0$ 行第 $0$ 列算起(方便求组合数),则操作 $k$ 次后,第 $i$ 个数 $=$ $C_{k+i-2}^{i-1}\times a_1+C_{k+i-3}^{i-2}\times a_2+C_{k+i-4}^{i-3}\times a_3+...+C_{k-1}^{0}\times a_i$,其中 $a$ 为初始序列。具体举例可以看 $pdf$。

系数反过来对应的原因是杨辉三角每个数的累加量是前缀和,而本题序列每个数的累加量是后缀和(前面的数离后面的数多远,前面的数就被算多少次)。

由于杨辉三角的对称性,反过来推序列也是一样的,每个数的计算公式 改一下上面那个就得到了。

由上述公式可知,第 $k$ 行的每个数被后面的数累加时的系数 只有 $n$ 种,又因为组合数 $C$ 的上面一项最多只有 $n$,所以可以预处理 $n$ 以内的逆元,再 $O(n^2)$ 暴力求出 $n$ 种系数(每个组合数最多是乘 $n$ 个数再除以 $n$ 个数,暴力计算的复杂度是 $O(n)$),最后用上述公式,把每个数前面的数都乘以对应的系数,累加即可求出这个数。时间复杂度 $O(n^2)$。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read()
 5 {
 6     int x = 0,f = 1;char ch = getchar();
 7     for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
 8     for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
 9     return x * f;
10 }
11 const int maxn = 5010,mod = 1e9 + 7;
12 int n,k;int a[maxn],s[maxn],Inv[maxn];
13 inline int ksm(int x,int t)
14 {
15     int res = 1;
16     while(t)
17     {
18         if(t & 1)res = res * x % mod;
19         x = x * x % mod;
20         t = t >> 1;
21     }
22     return res;
23 }
24 inline int inv(int x){return ksm(x,mod - 2);}
25 /*sig (1~x) from (x + k - i) choose (i) * a[0][i]*/
26 inline int C(int n,int m)
27 {
28     if(n < m || n < 0 || m < 0)return 0;
29     int cur = n,ans = 1;
30     for(int i=m;i>=1;i--)
31     {
32         (ans *= cur) %= mod;
33         (ans *= Inv[i]) %= mod;
34         cur--;
35     }
36     return ans;
37 }
38 int xs[maxn];
39 signed main()
40 {
41     freopen("sum.in","r",stdin);
42     freopen("sum.out","w",stdout);
43     n = read();k = read() - 1;
44     //for(int i=1;i<=n+k;i++)fac[i] = fac[i - 1] * i % mod;
45     Inv[0] = 1;
46     for(int i=1;i<=n;i++)Inv[i] = inv(i);
47     for(int i=1;i<=n;i++)a[i] = read();
48     //for(int i=1;i<=n;i++)a[i] -= a[i - 1];
49     //for(int i=1;i<=n;i++)cout<<a[i]<<" "; 
50     for(int i=1;i<=n;i++)xs[i] = C(i + k - 1,(i + k - 1) - k) % mod;
51     for(int i=1;i<=n;i++)
52         for(int j=1;j<=i;j++)
53             (s[i] += xs[i - j + 1] * a[j]) %= mod;
54     for(int i=1;i<=n;i++)printf("%lld ",s[i]);
55 }
View Code

 

T3

没好好想,瞎搞了一个时间复杂度不对的线段树就交上去了。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define N 100002
 4 #define M 200002
 5 #define inf 1000000000
 6 using namespace std;
 7 inline int read(){
 8     int x=0; bool f=1; char c=getchar();
 9     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
10     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
11     if(f) return x;
12     return 0-x;
13 }
14 int n,m,k;
15 ll ans;
16 struct input{
17     int r,s,t;
18     inline bool operator <(const input x)const{
19         return r>x.r;
20     }
21 }in[N];
22 int L,R;
23 namespace SegmentTree{
24     struct Tree{
25         int tag,mx,mn;
26     }tr[M<<3];
27     ll num[M];
28     inline void pushdown(int o){
29         if(tr[o].tag)
30             tr[o<<1].mx+=tr[o].tag, tr[o<<1].mn+=tr[o].tag,
31             tr[o<<1|1].mx+=tr[o].tag, tr[o<<1|1].mn+=tr[o].tag,
32             tr[o<<1].tag+=tr[o].tag, tr[o<<1|1].tag+=tr[o].tag, tr[o].tag=0;
33     }
34     inline void pushup(int o){tr[o].mx=max(tr[o<<1].mx,tr[o<<1|1].mx), tr[o].mn=min(tr[o<<1].mn,tr[o<<1|1].mn);}    
35     void update(int o,int l,int r){
36         //printf("udpate %d %d %d %d\n",L,l,r,R);
37         if(L<=l && r<=R){
38             ++tr[o].tag, ++tr[o].mx, ++tr[o].mn;
39             return;
40         }
41         pushdown(o);
42         int mid=(l+r)>>1;
43         if(L<=mid) update(o<<1,l,mid);
44         if(R>mid) update(o<<1|1,mid+1,r);
45         pushup(o);
46     }
47     ll query(int o,int l,int r){
48         if(tr[o].mx==tr[o].mn){
49             //printf("success %d %d\n",l,r);
50             tr[o].mx=tr[o].mn=-inf, tr[o].tag=0;
51             return r-l+1;
52         }
53         //printf("tag:%d\n",tr[o].tag);
54         pushdown(o);
55         //printf("revo %d %d %d %d %d %d\n",l,r,tr[o].mx,tr[o<<1].mx,tr[o<<1|1].mx,tr[o].mn);
56         int mid=(l+r)>>1; ll ret=0;
57         if(tr[o<<1].mx==tr[o].mx) ret+=query(o<<1,l,mid);
58         if(tr[o<<1|1].mx==tr[o].mx) ret+=query(o<<1|1,mid+1,r);
59         pushup(o);
60         return ret;
61     }
62 };
63 using namespace SegmentTree;
64 inline void action(int i,int l,int r){
65     //printf("action:%d %d %d\n",i,l,r);
66     L=l,R=r;
67     update(1,1,m);
68     if(tr[1].mx==k) ans+=query(1,1,m)*in[i].r*in[i].r;
69 //    printf("%lld\n",ans);
70 }
71 int main(){
72     freopen("xiaoqiao.in","r",stdin);
73     freopen("xiaoqiao.out","w",stdout); 
74     n=read(),m=read(),k=read();
75     int i;
76     for(i=1;i<=n;++i) in[i].r=read(), in[i].s=read()+m+1, in[i].t=read()+m;
77     sort(in+1,in+n+1);
78     m<<=1;
79     for(i=1;i<=n;++i){
80         if(in[i].s>in[i].t) action(i,in[i].s,m), action(i,1,in[i].t);
81         else action(i,in[i].s,in[i].t);
82     }
83     printf("%lld\n",ans);
84     return 0;
85 }
View Code

容易发现 $query$ 的复杂度错了,不是 $log$ 级别的,然后就就被爆踩。

由于只需要求最终的全局情况,也就是说答案跟操作顺序没关系,我们可以搞离线。

于是想到扫描线。

扫描线是个啥东西?如图

可以把问题同义转化一下:在墙上贴 $n$ 张底线相同、高度不同的海报,问有多大面积的区域至少被 $k$ 张海报覆盖。

然后我们在左端点搞一条扫描线(黑线)。

 

把扫描线不停往右移动,扫到下一个海报的左端点时,更新一下当前纵轴上的覆盖海报数(不能只 $+1$,因为可能有多个海报的左端点重合)。如果海报数 $\ge k$,就二分出从下往上数第 $k$ 个海报端点的位置,它下面就是覆盖海报数 $\ge k$ 的区域。

当扫描线扫到下一个位置时,累加它到上一个扫到的位置围出的 覆盖海报数 $\ge k$ 的区域面积,并再做上述更新即可。

所以我们把每张海报拆成左右两个端点,现在问题就变成了维护一个数据结构,支持单点插入、单点删除、查询第 $k$ 大值。

复杂度基本是对的,因为最多有 $2m$ 张海报,也就是 $4m=400000$ 个端点,那总共就有 $400000$ 次插入、删除操作(两个加起来)。

查询第 $k$ 大值就看卡常优不优秀了。这是个比较板子的维护,一种方法是树状数组二分(弱智写法的总时间复杂度是 $O(n*log(n^2))$,优秀写法的总时间复杂度是 $O(n*log(n))$),一种方法是平衡树(韩神现场yy了一个没写过的 $SBT$ 就 $A$ 了;总时间复杂度 $O(n*log(n))$ 带常数),还有一种方法是权值线段树(树上自带二分,总时间复杂度 $O(n*log(high))$)。

由于树状数组的常数小,所以跑的甚至比平衡树快一点……

  1 #include<bits/stdc++.h>
  2 #define LL long long
  3 using namespace std;
  4 inline int read()
  5 {
  6     int x = 0,f = 1;char ch = getchar();
  7     for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
  8     for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
  9     return x * f;
 10 }
 11 const int maxn = 400005;
 12 int n,m,kk;
 13 int top1,top2;
 14 struct qwq{int l,r,R;}nega[maxn],posi[maxn];
 15 struct ques{int st,r;char f;bool operator < (const ques &b)const{return st<b.st;}}opts[maxn];
 16 
 17 namespace RP
 18 {
 19     struct node{int ls,rs,size,val;}tr[maxn << 2];
 20     int dfn,root,ops;
 21     inline void pushup(int x){tr[x].size=tr[tr[x].ls].size+tr[tr[x].rs].size+1;}
 22     inline void lt(int &x)
 23     {
 24         int k=tr[x].rs;tr[x].rs=tr[k].ls;
 25         tr[k].ls=x;tr[k].size=tr[x].size;
 26         pushup(x);
 27         x=k;
 28     }
 29     inline void rt(int &x)
 30     {
 31         int k=tr[x].ls;tr[x].ls=tr[k].rs;
 32         tr[k].rs=x;tr[k].size=tr[x].size;
 33         pushup(x);
 34         x=k;
 35     }
 36     inline int rng()
 37     {
 38         static unsigned long long seed = 233333333333333333LL;
 39         seed = seed + (seed << 17) + 531433123515;
 40         return seed;
 41     }
 42     /*
 43     void rp()
 44     {
 45         int x;
 46         for(int i=1;i<=min(n,10);i++)lt(x = rng() % n + 1);
 47         for(int i=1;i<=min(n,10);i++)rt(x = rng() % n + 1);
 48     }
 49     */
 50     inline void magic(int &x,char f)
 51     {
 52         if(f)
 53         {
 54             if(tr[tr[tr[x].ls].ls].size > tr[tr[x].rs].size) rt(x),rt(x);
 55             else if(tr[tr[tr[x].ls].rs].size > tr[tr[x].rs].size) lt(tr[x].ls),rt(x);
 56             else return;
 57         }
 58         else
 59         {
 60             if (tr[tr[tr[x].rs].rs].size > tr[tr[x].ls].size) lt(x);
 61             else if (tr[tr[tr[x].rs].ls].size > tr[tr[x].ls].size) rt(tr[x].rs),lt(x);
 62             else return;
 63         }
 64         if(rng() & 1)lt(x); //Lucky!
 65         magic(tr[x].ls,0);magic(tr[x].rs,1);
 66         return;
 67     }
 68     inline void Insert(int &x,int v)
 69     {
 70         if (!x)
 71         {
 72             x = ++dfn;
 73             tr[x].ls = tr[x].rs = 0;
 74             tr[x].size = 1;
 75             tr[x].val = v;
 76             //rp();
 77         }
 78         else
 79         {
 80             tr[x].size++;
 81             if (v<tr[x].val) Insert(tr[x].ls,v);
 82             else Insert(tr[x].rs,v);
 83             magic(x,v < tr[x].val);
 84         }
 85     }
 86     inline int del(int &x,int v)
 87     {
 88         tr[x].size--;
 89         int k = tr[x].val;
 90         if (v == k || (v < k && !tr[x].ls) || (v > k && !tr[x].rs) )
 91         {
 92             if (!tr[x].ls || !tr[x].rs) x = tr[x].ls + tr[x].rs;
 93             else tr[x].val = del(tr[x].ls,v + 1);
 94             return k;
 95         }
 96         else
 97         {
 98             if (v < k) return del(tr[x].ls,v);
 99             else return del(tr[x].rs,v);
100         }
101     }
102     inline int kth(int k)
103     {
104         int x = root;
105         while(tr[tr[x].ls].size + 1 != k)
106         {
107             if(tr[tr[x].ls].size+1 < k) k -= tr[tr[x].ls].size,k--,x = tr[x].rs;
108             else x = tr[x].ls;
109         }
110         return tr[x].val;
111     }
112 }
113 using namespace RP;
114 int main()
115 {
116     freopen("xiaoqiao.in","r",stdin);
117     freopen("xiaoqiao.out","w",stdout);
118     n = read(),m = read(),kk = read();
119     for(int i=1;i<=n;i++)
120     {
121         int R = read(),l = read(),r = read();
122         if(l == m) l = -m;
123         if(r == -m) r = m;
124         if(l == r) continue;
125         if(l >= 0 && r >= 0)
126         {
127             if(l > r)
128             {
129                 if(m) nega[++top1]=(qwq){-m,0,R};
130                 if(l != m) posi[++top2]=(qwq){l,m,R};
131                 
132                 if(r) posi[++top2]=(qwq){0,r,R};
133             }
134             else posi[++top2]=(qwq){l,r,R};
135         }
136         else if(l <= 0 && r <= 0)
137         {
138             if(l > r)
139             {
140                 if(l) nega[++top1]=(qwq){l,0,R};
141                 if(r != -m) nega[++top1]=(qwq){-m,r,R};
142                 
143                 if(m) posi[++top2]=(qwq){0,m,R};
144             }
145             else nega[++top1]=(qwq){l,r,R};
146         }
147         else if (l <= 0 && r >= 0)
148         {
149             if (l) nega[++top1]=(qwq){l,0,R};
150             if (r) posi[++top2]=(qwq){0,r,R};
151         }
152         else if(l >= 0 && r <= 0)
153         {
154             nega[++top1]=(qwq){-m,r,R};
155             posi[++top2]=(qwq){l,m,R};
156         }
157     }
158     LL res1 = 0,res2 = 0;
159     
160     root = dfn = ops = 0;
161     for(int i=1;i<=top1;i++)
162     {
163         opts[++ops] = (ques){nega[i].l,nega[i].R,1};
164         opts[++ops] = (ques){nega[i].r,nega[i].R,0};
165     }
166     sort(opts + 1,opts + ops + 1);
167     int last = opts[1].st;
168     int l = 1,r = 1;
169     while(l <= ops)
170     {
171         while(r <= ops && opts[r].st == opts[l].st) r++;
172         if(tr[root].size >= kk)
173         {
174             LL x = kth(tr[root].size - kk + 1); //di k da = n - k + 1 xiao
175             res1 += x * x * (opts[l].st - last);
176         }
177         for(int i=l;i<r;i++)
178         {
179             if(opts[i].f == 1) Insert(root,opts[i].r);
180             else del(root,opts[i].r);
181         }
182         last = opts[l].st;
183         l = r;
184     }
185     
186     root = dfn = ops = 0;
187     for(int i=1;i<=top2;i++)
188     {
189         opts[++ops] = (ques){posi[i].l,posi[i].R,1};
190         opts[++ops] = (ques){posi[i].r,posi[i].R,0};
191     }
192     sort(opts + 1,opts + ops + 1);
193     last = opts[1].st;
194     l = 1,r = 1;
195     while(l <= ops)
196     {
197         while(r <= ops && opts[r].st == opts[l].st) r++;
198         if(tr[root].size >= kk)
199         {
200             LL x = kth(tr[root].size - kk + 1); //di k da = n - k + 1 xiao
201             res2 += x * x * (opts[l].st - last);
202         }
203         for(int i=l;i<r;i++)
204         {
205             if(opts[i].f == 1) Insert(root,opts[i].r);
206             else del(root,opts[i].r);
207         }
208         last = opts[l].st;
209         l = r;
210     }
211     cout<<res1 + res2;
212 }
韩神的SBT
 1 #include<bits/stdc++.h>
 2 #define rep(i,x,y) for(int i=(x);i<=(y);++i)
 3 #define dwn(i,x,y) for(int i=(x);i>=(y);--i)
 4 #define LL long long
 5 #define maxn 200010
 6 using namespace std;
 7 int read()
 8 {
 9     int x=0,f=1;char ch=getchar();
10     while(!isdigit(ch)&&ch!='-')ch=getchar();
11     if(ch=='-')ch=getchar(),f=-1;
12     while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
13     return x*f;
14 }
15 void write(LL x)
16 {
17     if(x==0){putchar('0'),putchar('\n');return;}
18     if(x<0)putchar('-'),x=-x;
19     int f=0;char ch[20];
20     while(x)ch[++f]=x%10+'0',x/=10;
21     while(f)putchar(ch[f--]);
22     putchar('\n');
23     return;
24 }
25 int n,m,k,qr,qs,qt,tr[maxn];
26 int tpr;
27 LL ans;
28 int lt(int x){return x&(-x);}
29 void add(int x,int k){for(;x<=tpr;x+=lt(x))tr[x]+=k;return;}
30 int ask(int x){int k=0;for(;x;x-=lt(x))k+=tr[x];return k;}
31 vector<int>ad[maxn],de[maxn];
32 int gx(int x){return m-x;}
33 int nxt(int x){if(x>(m<<1))return x-(m<<1);if(x<1)return (m<<1)-x;return x;}
34 int main()
35 {
36     freopen("xiaoqiao.in","r",stdin);
37     freopen("xiaoqiao.out","w",stdout);
38     n=read(),m=read(),k=read();
39     rep(i,1,n)
40     {
41         qr=read(),qs=read(),qt=read();
42         qs=gx(qs),qt=gx(qt),qt=nxt(qt+1),qs=nxt(qs),swap(qs,qt);
43         if(qs<=qt)
44         {
45             ad[qs].push_back(qr),de[qt+1].push_back(qr);
46         }
47         else 
48         {
49             ad[1].push_back(qr),de[qt+1].push_back(qr);
50             ad[qs].push_back(qr),de[m*2+1].push_back(qr);
51         }
52         tpr=max(tpr,qr);
53     }tpr++;int li=m*2;
54     rep(i,1,li)
55     {
56         int lim=ad[i].size()-1;
57         rep(j,0,lim)add(1,1),add(ad[i][j]+1,-1);
58         lim=de[i].size()-1;
59         rep(j,0,lim)add(1,-1),add(de[i][j]+1,1);
60         int L=1,R=tpr,maxl=0;
61         while(L<=R)
62         {
63             int mid=(L+R>>1);
64             int tmp=ask(mid);
65             if(tmp>=k)maxl=max(maxl,mid),L=mid+1;
66             else R=mid-1;
67         }
68         ans+=(LL)maxl*(LL)maxl;
69     }
70     write(ans);
71     return 0;
72 }
石神的二分树状数组
posted @ 2018-11-02 18:47  大本营  阅读(212)  评论(0编辑  收藏  举报