[考试反思]0110省选模拟4: 苟活

不行了,做不动题了,写博客吧。。。

啊。。。全天效率低下。。。状态很差。。。莫名累。。。

而且还赶上数据结构专题测试(?)。能活着不滚到10名开外简直是莫大的荣幸2333

如何在什么也不会的情况下骗下T1?DC骗分录持续更新。(雾

骗分的前置知识:C++语言基础,STL,歪心思

看到T1,啊,二维几何,K-D啊。呃。。。直线距离而不是曼哈顿?好像很难写?算了算了估计也写不出来

$O(n^2)$爆扫还是会的。判断是否有包含关系。。。嗯。。。太暴力了

应该还是可以优化的。。?如果两个点横坐标的差大于半径了,那么直线距离就一定大于半径,不可能包含了。。。

于是按横坐标排一下序,这样也许能缩小一点枚举范围?

但是需要一个支持插入,删除的有序数据结构。。。STLset,但是比暴力还多一个$log$?

写写吧。于是你就有了比纯暴力的$60$分多$16$分。

但是

你觉得出题人会这么简单?他不想卡你?

他那么毒瘤肯定会卡你啊!他会想到你这个想法然后针对你构造数据啊!

那怎么办?

你跟我耍心机?那我只好反击了?

你觉得我会按照横坐标排序?不!我就要按着纵坐标排序!

你要卡我?那你还卡不卡那多数按照横坐标排序的人?

然后做好心理准备拿60分就可以走了,写个全场最短的代码扬长而去

然后你就AC了2333!

靠着这种乱七八糟毫无依据的方法多拿了24分。我也就会干这种事了。

揣测出题人意图?反向心理???

然而现在要揭示我有多智障了。

T1AC,T2T3全爆零,这是个啥啊????

首先看一下我T2的根号筛质因子

看起来人畜无害?对iv进行了质因数分解。

但是为什么mul%p==0,为什么是mul啊?

啊你问我?我不知道啊!我是智障吧。

我是怎么过样例的????还过了手模的一个大点的点???

然后就这么爆零了。

T3依旧弱智。。。我把题当成单向边的做了不知道多久

然后冲着我的网络流看了半天我板子怎么炸了

难得没炸一次啊啊啊又把分送了

我又是怎么过样例的啊!!!

如果这两个弱智分拿到了就180了。。。也是个rk2啊。。。

然而没有梦想rk8得了。。。

弱智在数据结构专题混个rk8也不容易,就这样吧。。。

T1:点点的圈圈

题目大意:给出若干可包含但不相交的圆,选中一些互不包含的圆最大化总价值。$n \le 10^5,w_i \le 1000,x_i,y_i,r_i \le 10^8$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 100005
 4 struct cir{
 5     int x,y,r,w,o;
 6     friend bool operator<(cir a,cir b){return a.y!=b.y?a.y<b.y:a.o<b.o;}
 7 }C[S];
 8 bool cmp(cir a,cir b){return a.r<b.r;}
 9 int n,f[S],del[S],dp[S],cc[S],q[S],qt;
10 long long x2(int a){return 1ll*a*a;}
11 set<cir>s;
12 int main(){//freopen("1.in","r",stdin);
13     scanf("%d",&n);
14     for(int i=1;i<=n;++i)scanf("%d%d%d%d",&C[i].x,&C[i].y,&C[i].r,&C[i].w);
15     sort(C+1,C+1+n,cmp);
16     for(int i=1;i<=n;++i){
17         C[i].o=i; cir R=C[i],t; R.y-=C[i].r;
18         auto p=s.lower_bound(R);int tp=0;
19         while(p!=s.end()&&(*p).y<=C[i].y+C[i].r){
20             t=*p;p++;
21             if(x2(R.x-t.x)+x2(C[i].y-t.y)<=x2(R.r))f[t.o]=i,del[++tp]=t.o;
22         }
23         for(int i=1;i<=tp;++i)s.erase(C[del[i]]);
24         s.insert(C[i]);
25     }
26     for(int i=1;i<=n;++i)cc[f[i]]++;
27     for(int i=1;i<=n;++i)if(!cc[i])q[++qt]=i;
28     for(int h=1;h<=qt;++h){
29         dp[q[h]]=max(dp[q[h]],C[q[h]].w);
30         if(q[h])dp[f[q[h]]]+=dp[q[h]]; cc[f[q[h]]]--;
31         if(!cc[f[q[h]]])q[++qt]=f[q[h]];
32     }cout<<dp[0];
33 }
乱搞

还是要运用那种「相含不相交就想父子关系」的思想建树。难点在建树。KD肯定是可以的但是不够优秀(虽然跑的更快)

正解是$O(nlogn)$的。运用了扫描线的思想,将圆的最左右两端坐标离散化后扫,现在只有两种事件。

1,插入一个圆。

如果你扫到某个圆的左端点,那么它就该出现了。

插入的时候可以顺便找一下包含它的最小圆。你可以找到纵坐标离它最近的那一个。

但是它可能是你的兄弟。。。怎么办?

于是把圆拆成上下两半加以区分。查纵坐标更大的点,如果查到下半圆那它就是你的兄弟,你和它一个爹。否则上半圆那就是你爹了。

打上一个标记,在这个圆到达右端点时删除。

2,删除一个圆。

也就是插入,删除,查前驱后继,STLset没问题了。

重载小于号。开一个全局变量表示当前扫描线所在的横坐标,计算半圆对应的纵坐标。

因为圆不相交,所以上下半圆之间的纵坐标虽然在变化,但是它们的相对大小关系是不变的,所以set不会出锅。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 200005
 4 int X,n,f[S],dp[S],fir[S],l[S],to[S],r[S],rc,I=1,ec=1;vector<int>v[S];
 5 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
 6 struct P{int x,y,r,w;friend bool operator<(P a,P b){return a.x-a.r<b.x-b.r;}}p[200005];
 7 void dfs(int x){for(int i=fir[x];i;i=l[i])dfs(to[i]),dp[x]+=dp[to[i]];dp[x]=max(dp[x],p[x].w);}
 8 long long x2(int p){return 1ll*p*p;}
 9 struct Q{
10     int i,k;
11     double Y(int x){return p[i].y+k*sqrt(x2(p[i].r)-x2(p[i].x-x))+k*0.001;}
12     friend bool operator<(Q x,Q y){return x.Y(X)<y.Y(X);}
13 }R;set<Q>s;
14 int main(){
15     cin>>n;for(int i=1;i<=n;++i)scanf("%d%d%d%d",&p[i].x,&p[i].y,&p[i].r,&p[i].w);
16     sort(p+1,p+1+n);
17     for(int i=1;i<=n;++i)r[++rc]=p[i].x-p[i].r,r[++rc]=p[i].x+p[i].r;
18     sort(r+1,r+1+rc);rc=unique(r+1,r+1+rc)-r-1;
19     for(int _=1;X=r[_],_<=rc;++_){
20         for(int i=0;i<v[_].size();++i)s.erase((Q){v[_][i],-1}),s.erase((Q){v[_][i],1});
21         while(I<=n&&p[I].x-p[I].r==X){
22             auto it=s.lower_bound((Q){I,-1});
23             if(it!=s.begin())it--,f[I]=(*it).k==1?f[(*it).i]:(*it).i;
24             v[lower_bound(r+1,r+1+rc,p[I].x+p[I].r)-r].push_back(I);
25             s.insert((Q){I,1});s.insert((Q){I,-1});I++;
26         }
27     }for(int i=1;i<=n;++i)link(f[i],i);dfs(0);cout<<dp[0];
28 }
%%%AKt

T2:点点的计算

题目大意:$T(i,1)=i,T(i,j)=\frac{1}{\frac{1}{T(i,j-1)}-\frac{1}{T(i-1,j-1)}}$。求$lcm(T(n,i))\ (1 \le i \le k)$。$70\%$的数据强制在线。

$T \le 2\times 10^5,n,k\le 10^5$

打表发现$T(i,j)=i\times C_{i-1}^{j-1}$。然而没有什么帮助。

大神们都看出了结论:$ans=lcm(n-k+1,...,n)$。

可以证明,先证明$lcm(T(i,j),T(i,j-1))=lcm(T(i-1,j-1),T(i,j))$。证明过程中要写一大堆分数懒得再写一遍$LATEX$了。

把$T(i,j)$拆开,发现式子只与$T(i-1,j-1)$与$T(i,j-1)$有关。换个变量表示要证明的就变成了$lcm(a,b)=lcm(a,\frac{ab}{(a-b)})$

化$lcm$为$gcd$,再把$g=gcd(a,b),a=Ag,b=Bg$代替已有变量能得到只需要证$Ag=(A-B)gcd(\frac{ABg}{A-B},Ag)$

把右端的$A-B$乘进$gcd$里面,问题在于证明$gcd(AgB,Ag(A-B))=Ag$即$gcd(B,A-B)=1$

而因为$A,B$是由$a,b$提公因子得到,所以它们已经互质。$A \perp B \rightarrow (A-B) \perp B$

所以最开始的那个式子得证。然后就把$j$这一列的问题推到了$j-1$列。同理一直推到第$1$列,而$T(i,1)=i$

所以得证。

问题变为维护$lcm(a...b)$。连续自然数的最小公倍数。

每个因子$p^k$只有在第一次出现的时候贡献答案,考虑每加入一个数的贡献。

对于$b=1$时显然。开线段树维护区间积。考虑当$b$增加时线段树的变化。

其实只有$b$的因子发生了变化,它们的第一次出现位置发生了改变。原来在$b-p^k$,现在在$b$。

于是在$b-p^k$去掉贡献,在$b$作出贡献就行了。

离线只要排序,一边处理询问一边更新线段树。

在线的话把线段树可持久化就行。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1000000007
 4 #define S 10000005
 5 int pow(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 6 int iv[S],c[S],d[S],w[S],rt[S],pc,lc[S],rc[S],la;
 7 void build(int p,int cl=1,int cr=100000){
 8     w[p]=1;if(cl==cr)return;
 9     lc[p]=p<<1;rc[p]=p<<1|1;
10     build(p<<1,cl,cl+cr>>1);
11     build(p<<1|1,(cl+cr>>1)+1,cr);
12 }
13 void modify(int cpy,int &p,int ps,int v,int cl=1,int cr=100000){
14     w[p=++pc]=1ll*w[cpy]*v%mod;lc[p]=lc[cpy];rc[p]=rc[cpy];
15     if(cl==cr)return;
16     if(ps>cl+cr>>1)modify(rc[cpy],rc[p],ps,v,(cl+cr>>1)+1,cr);
17     else modify(lc[cpy],lc[p],ps,v,cl,cl+cr>>1);
18 }
19 int ask(int l,int r,int p,int cl=1,int cr=100000){
20     if(l<=cl&&cr<=r)return w[p];
21     return 1ll*(l<=cl+cr>>1?ask(l,r,lc[p],cl,cl+cr>>1):1)*(r>cl+cr>>1?ask(l,r,rc[p],(cl+cr>>1)+1,cr):1)%mod;
22 }
23 int main(){//freopen("1.in","r",stdin);
24     int n,k,q,a,b,mo;cin>>q>>n>>k>>a>>b>>mo;
25     for(int i=1;i<q;++i)scanf("%d",&c[i]);
26     for(int i=1;i<q;++i)scanf("%d",&d[i]);
27     for(int i=1;i<100001;++i)iv[i]=pow(i,mod-2);
28     rt[1]=1;build(1);pc=400000;
29     for(int i=2;i<=100000;++i){
30         int I=i,ls=i-1;
31         for(int j=2;j*j<=I;++j)if(I%j==0){
32             int tms=0,al=1;
33             while(I%j==0)I/=j,tms++,al*=j;
34             if(al==i)al/=j;
35             while(al!=1)modify(rt[ls],rt[i],i-al,iv[j]),ls=i,al/=j;
36         }
37         if(I!=1&&i!=I)modify(rt[ls],rt[i],i-I,iv[I]),ls=i;
38         modify(rt[ls],rt[i],i,i);
39     }
40     for(int i=1;i<=q;++i)printf("%d\n",la=ask(n-k+1,n,rt[n])),n=(1ll*a*la+c[i])%mo+1,k=(1ll*b*la+d[i])%n+1;
41 }
线段树板子没炸,kx

T3:点点的最大流

题目大意:仙人掌,改边权,指定源汇查询最大流。$n \le 10^5,m,q \le 2 \times 10^5$

对于$30\%$的数据是一棵树。

最大流是最小割,树的话直接查路径最小边权。树剖就行。

仙人掌就是多了简单环,要么断缩点后图的边,要么断某个环上的两条边。

缩点+线段树+树剖。断环成链,线段树维护。

看得不是很明白。大概意思是点权是每个点和新树父点的顶端的最小割值查询最小值。

重链树剖维护,轻边线段树查询,复杂度两个log。

代码十分恶心?先撂了。

猜你喜欢

转载自www.cnblogs.com/hzoi-DeepinC/p/12178219.html