zoukankan      html  css  js  c++  java
  • 【2019.3.20】NOI模拟赛

    题目

    这里必须标记一下那个傻逼问题,再不解决我人就没了!

    先放一个 $T3$ $20$ 分暴力

      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 rep_e(i,u) for(int i=hd[u];i;i=e[i].nxt)
      5 #define lc tr[o].l
      6 #define rc tr[o].r
      7 #define N 100003
      8 #define inf 2147483647
      9 using namespace std;
     10 inline int read(){
     11     int x=0; bool f=1; char c=getchar();
     12     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
     13     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
     14     if(f) return x;
     15     return 0-x;
     16 }
     17 
     18 int T1_ROOT=1;
     19 int n,m,L1,R1,L2,R2,W,ans;
     20 
     21 struct edge{int u,v,w;}e[2002*2002];
     22 int cnt;
     23 inline bool cmp(edge a,edge b){return a.w<b.w;}
     24 
     25 struct Tree{int l,r,v;}tr[N*150];
     26 int rt[N<<1],tot[3];
     27 inline int newNode(int x){return ++tot[x];}
     28 inline void pushdown(int o){
     29     if(tr[o].v){
     30         tr[lc].v+=tr[o].v, tr[rc].v+=tr[o].v;
     31         tr[o].v=0;
     32     }
     33 }
     34 void add2(int &o,int l,int r){
     35     if(!o) o=newNode(2);
     36     if(L2<=l && r<=R2){tr[o].v+=W; return;}
     37     pushdown(o);
     38     int mid=(l+r)>>1;
     39     if(L2<=mid) add2(lc,l,mid);
     40     if(R2>mid) add2(rc,mid+1,r);
     41 }
     42 void add1(int &o,int l,int r){
     43     if(!o) o=newNode(1);
     44     //printf("%d %d %d %d %d
    ",o,l,r,L1,R1);
     45     //system("pause");
     46     if(L1<=l && r<=R1){add2(rt[o],1,n); return;}
     47     int mid=(l+r)>>1;
     48     if(L1<=mid) add1(lc,l,mid);
     49     if(R1>mid) add1(rc,mid+1,r);
     50 }
     51 int X,mat[2002][2002];
     52 void dfs2(int o,int l,int r){
     53     if(!o) return;
     54     if(l==r){mat[X][l]+=tr[o].v; return;}
     55     pushdown(o);
     56     int mid=(l+r)>>1;
     57     dfs2(lc,l,mid);
     58     dfs2(rc,mid+1,r);
     59 }
     60 void dfs1(int o,int l,int r){
     61     if(!o) return;
     62     rep(i,l,r) X=i, dfs2(rt[o],1,n);
     63     if(l==r) return;
     64     int mid=(l+r)>>1;
     65     dfs1(lc,l,mid);
     66     dfs1(rc,mid+1,r);
     67 }
     68 /*
     69 struct SegTree{
     70     struct TREE{int l,r,v;}tr[N*90];
     71     int L,R,W,e[2001][2001];
     72     
     73     inline void pushdown(int o){
     74         if(tr[o].v){
     75             tr[lc].v+=tr[o].v, tr[rc].v+=tr[o].v;
     76             tr[o].v=0;
     77         }
     78     }
     79     void add(int &o,int l,int r){
     80         if(!o) o=newNode(1);
     81         if(L1<=l && r<=R1){tr[o].v+=W; return;}
     82         pushdown(o);
     83         int mid=(l+r)>>1;
     84         if(L1<=mid) add1(lc,l,mid);
     85         if(R1>mid) add1(rc,mid+1,r);
     86     }
     87     inline void add(int ver,int l,int r,int w){
     88         L=l, R=r, W=w; add(rt[ver],1,n);
     89     }
     90 }
     91 */
     92 int fa[N];
     93 int find(int x){return x==fa[x] ? x : fa[x]=find(fa[x]);}
     94 namespace pts20{
     95     void solve(){
     96         rep(i,1,m)
     97             L1=read(), R1=read(), L2=read(), R2=read(), W=read(), add1(T1_ROOT,1,n);
     98         dfs1(1,1,n);
     99         rep(i,1,n)
    100             rep(j,i+1,n)
    101                 e[cnt++]=(edge){i,j,mat[i][j]};
    102         sort(e,e+cnt,cmp);
    103         rep(i,1,n) fa[i]=i;
    104         int e_cnt;
    105         for(int i=0;i<cnt;++i){
    106             int fu=find(e[i].u), fv=find(e[i].v);
    107             //printf("zuixiao:%d %d %d
    ",e[i].u,e[i].v,e[i].w);
    108             if(fu==fv) continue;
    109             fa[fv]=fu;
    110             ans+=e[i].w;
    111             ++e_cnt;
    112             //printf("%d %d  %d %d
    ",e_cnt,n-1,i,cnt); 
    113             if(e_cnt>=n-1) break;
    114         }
    115         printf("%d
    ",ans);
    116     }
    117 }
    118 using namespace pts20;
    119 /*
    120 int mn_o,mn_v;
    121 void queryMin(int o,int l,int r){
    122     if(!o) return;
    123     if(l==r){mn_o=o, mn_v=tr[o].mn; return;}
    124     int mid=(l+r)>>1;
    125     if(tr[lc].mn<=tr[rc].mn) queryMin(lc,l,mid);
    126     else queryMin(rc,mid+1,r);
    127 }
    128 int blo;
    129 */
    130 int main(){
    131     //freopen("C.in","r",stdin);
    132     //freopen("C.out","w",stdout);
    133     n=read(), m=read();
    134     tot[1]=1, tot[2]=n<<1;
    135     if(n<=2000) pts20::solve();
    136     return 0;
    137 }
    138 /*
    139 5 4
    140 1 2 3 4 10
    141 1 1 2 2 -20
    142 3 3 4 4 -5
    143 2 2 5 5 -15
    144 */
    View Code

    写了个树套树连边,标准输入输出随便过样例,真爽。

    然而加了个文件读写,测样例就输出 $-20$ 了。

    我都忘了这种问题是什么情况了,考后重新输出中间结果查了一遍,发现真是石乐志。

    注意第 $104$ 行,局部变量没有赋初值。没文件读写就默认把初值弄成 $0$,有文件读写就给了个随机初值。

    这个问题其实大多数人都会犯,但我是真的想不起来这种错误了,先立个 $flag$,下次再出这种问题查不出来的话我得吃点什么。


    T1

    30pts

    没想

    50pts

    我们把区间看成竖直的,第 $i$ 个区间代替成平面直角坐标系中 从 $(i,L_i)$ 到 $(i,R_i)$ 的线段。问题就成了有多少种直线能穿过所有 $n$ 条线段。

    考虑暴力求斜率的上下界,然后枚举斜率,再扫一遍 $n$ 条线段,把每条直线的上下端点按斜率映射到 $y$ 轴上,最后这种斜率的直线穿过所有 $n$ 条线段的方案数 上端点映射在 $y$ 轴上的最小值 $-$ 就是下端点映射在 $y$ 轴上的最大值(这里注意特判一下,如果前者小于后者,方案数应该为 $0$)。

    由斜率上下界的计算方法(见代码)可知,时间复杂度大概是 $O(线段长度 / n imes n) = O(线段长度)$。

    100pts

    (下标从 $0$ 开始算)

    对于一条直线,我们用一个二元组 $(a,d)$ 表示,它的意义是直线与 $x=0$ 的交点的纵坐标为 $a$,直线的斜率为 $d$ 。

    那对于每条直线,若其满足条件,则要满足该约束:$a∈[-id+L_i,space -id+R_i]$

    也就是 $-id+L_ile ale -id+R_i$

    它的解的数量是一个以 $d$ 为横坐标,以 $a$ 为纵坐标的二维平面中两条直线所夹的范围内的整点数。

    然后总共有 $n$ 个约束,那么就有 $2 imes n$ 条直线。

    解的总数就是被所有对直线夹起来的那一部分 二维平面的整点数,从图上看,它就是最里面的那一块。

    半平面交?差不多就是求这种东西。

    画图举例。同色线条代表一组约束,那么半平面交的部分就是中间的灰色区域。我们要求那部分面积。

    求半平面交应该并不好写,但这题有个特殊性质,就是直线是一对一对地放到平面上的,我们只要分别对所有上界直线和下界直线求凸包就可以了。

    补充知识:维护凸包

    以上凸包为例,把所有边按斜率 $k$ 从大到小排序,用一个单调队列维护当前认为的凸包边界。比如已经往单调队列中插入 $3$ 条边的情况如下(灰色区域为凸包内部):

    插入第 $4$ 条直线(红线)时,判断单调队列最右端的两条直线的交点 与单调队列最右端一条直线与新插入的直线的交点 的横坐标,如果后者小于前者,就删掉单调队列最右端一条直线,并继续尝试往前删;否则将新插入的直线放在单调队列最右端。

    结合画图可理解,因为后者小于前者时,新插入的直线与当前凸包最右边的直线的交点在凸包外,故新插入的之前在凸包范围内 会与当前凸包更靠左的直线相交(显然这样会缩小凸包),当前凸包最右边的直线就被排除到凸包外了,故删除。

    下凸包同理。

    求完上界的上凸包和下界的下凸包后,仔细思考一下,发现多边形无法直接计算面积,所以我们把凸包拆成若干个三角形和四边形,显然这两种图形都能计算面积。具体实现:用扫描线按横坐标从小到大扫凸包,每遇到凸包的一个拐点(不管拐点在上界还是下界),都计算一下凸包这一部分的三角形/梯形面积(具体地说就是计算点数)。

     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 rep_e(i,u) for(int i=hd[u];i;i=e[i].nxt)
     5 #define ll long long
     6 #define M 200020
     7 #define inf 100000010
     8 #define maxL -inf
     9 #define maxR inf
    10 using namespace std;
    11 inline int read(){
    12     int x=0; bool f=1; char c=getchar();
    13     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    14     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    15     if(f) return x;
    16     return 0-x;
    17 }
    18 int n,m,m1,m2;
    19 ll X[(M<<1)+10],c[2][M],ans;
    20 struct line{
    21     ll k,b;
    22     line(){}
    23     line(ll _k,ll _b){k=_k,b=_b;}
    24     ll get(ll x){return k*x+b;}
    25 }mx[M],mn[M],s[2][M];
    26 inline ll con(line &A,line &B){return (ll)ceil((double)(A.b-B.b)/(double)(B.k-A.k));}
    27 int build(line *x,line *S,ll *C){
    28     int top=1; S[1]=x[1];
    29     rep(i,2,n){
    30         while(top>1 && con(S[top],S[top-1])>=con(S[top],x[i])) --top;
    31         S[++top]=x[i];
    32     }
    33     rep(i,1,top)
    34         C[i]=i>1?con(S[i],S[i-1]):maxL, X[++m]=C[i];
    35     return top;
    36 }
    37 ll calc(line up,line dn,int l,int r){
    38     ll len=r-l;
    39     if(up.get(l)>=dn.get(l) && up.get(r)>=dn.get(r))
    40         return (up.get(l)-dn.get(l)+1)*(len+1)+(len*(len+1)>>1)*(up.k-dn.k);
    41     if(up.get(l)==dn.get(l) || up.get(r)==dn.get(r)) return 1ll; //构造出的任意两条直线的交点必定是个整点,所以反三角的顶点处必定是个整点 
    42     if(up.get(l)<dn.get(l) & up.get(r)<dn.get(r)) return 0ll;
    43     ll pos=con(up,dn); return calc(up,dn,l,pos-1)+calc(up,dn,pos,r);
    44 }
    45 void solve(){
    46     sort(X+1,X+m+1);
    47     for(int i=2,t1=1,t2=1; i<=m; ++i) if(X[i]>X[i-1]){
    48         while(t1<m1 && c[0][t1+1]<=X[i-1]) t1++;
    49         while(t2<m2 && c[1][t2+1]<=X[i-1]) t2++;
    50         ans+=calc(s[0][t1],s[1][t2],X[i-1],X[i]-1);
    51     }
    52 }
    53 int main(){
    54     n=read(), X[1]=-maxL, X[m=2]=maxR;
    55     int dn,up;
    56     rep(i,0,n-1){
    57         dn=read(), up=read();
    58         mx[i+1]=line(-i,up), mn[i+1]=line(-i,dn);
    59     }
    60     reverse(mn+1,mn+n+1);
    61     m1=build(mx,s[0],c[0]);
    62     m2=build(mn,s[1],c[1]);
    63     solve(), printf("%lld
    ",ans);
    64     return 0;
    65 }
    View Code

    T2

    10pts

    打表

    100pts

    这题题面出锅了,$pdf$ 开头说了下文题面不会有废话,但这题第二句实际上不但是废话还透露了题解……(虽然并不能看出来那是题解)

    首先,期望 $=$ 概率 $ imes$ 贡献,一个点最终的期望值,是由其它每个点由某个概率给来 $1$ 贡献得到的(也就是标记一个点时,标记点与这个点在同一连通块时才对这个点造成 $1$ 贡献,否则没有)。所以可以反过来考虑每一个点给其它点贡献的期望值,也就是造成贡献的概率。

    对于一对点 $x,y$,当我们标记点 $x$ 时,只有连接两点的简单路径上没有点被标记的情况下(也就是两点在同一连通块),点 $x$ 才会对点 $y$ 造成 $1$ 贡献(同一连通块所有点点权 $+1$)。

    既然连接两点的简单路径上所有点都没被标记过,那么点 $x$ 对点 $y$ 造成 $1$ 贡献的概率是 $frac{1}{dis(x,y)+1}$。

    因为在这条简单路径上的 $dis(x,y)+1$ 个点中,必须先选点 $x$ 才能对点 $y$ 造成 $1$ 贡献,若先选路径上其它一点,点 $x,y$ 就不在一个连通块,之后点 $x$ 就没法对点 $y$ 造成贡献了。

    注意:$x$ 可以等于 $y$,因为标记一个点本身就会对自己造成 $1$ 贡献。

    对于每一对点 $x,y$ 都是这样,所以题目要求的答案就是 $$sum_{i=1}^{n}sum_{j=1}^{n}frac{1}{dis(i,j)+1}$$

    也就是我们只需要统计对于 $i∈[1,n]$,求距离为 $i$ 的点对数。

    一说到统计树上所有的简单路径,自然有点分治。

    对于一个分治中心的两棵子树,开一个数组 $cnt$ 记录已经遍历过的子树中的点,以到重心的距离为下标,即 $cnt_i$ 表示已经遍历过的所有点中,到重心距离为 $i$ 的点数。

    由于路径长度的合并性质,到重心距离分别为 $i$ 和 $j$ 的两个不同子树中的点的距离为 $i+j$,换在数组上就是 $cnt$ 数组的第 $i$ 位和第 $j$ 位的乘积存在 $ans$ 数组的第 $i+j$ 位上($ans_i$ 表示在当前遍历过的点集中,简单路径经过当前分治中心 且距离为 $i$ 的点对的数量 ),所以用 $NTT$ 合并数组即可。

    复杂度 $O(n imes log^2(n))$(点分治共 $log$ 层,每层点数总和是 $n$ 级别的,合并这些点的复杂度是 $O(n imes log(n))$)。由于 $NTT$ 的大常数,开 $3$ 秒好像很有道理。

    有点恶心的题……

      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 rep_e(i,u) for(int i=hd[u];i;i=e[i].nxt)
      5 #define ll long long
      6 #define N 100003
      7 #define mod 998244353
      8 #define G 3
      9 using namespace std;
     10 inline int read(){
     11     int x=0; bool f=1; char c=getchar();
     12     for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
     13     for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
     14     if(f) return x;
     15     return 0-x;
     16 }
     17 int n,inv[N];
     18 struct edge{int v,nxt;}e[N<<1];
     19 int hd[N],cnt;
     20 inline void add(int u,int v){e[++cnt]=(edge){v,hd[u]}, hd[u]=cnt;}
     21 int siz[N],Siz,mn,root; bool vis[N];
     22 void getRoot(int u,int fa){
     23     siz[u]=1; int mxson=0;
     24     rep_e(i,u) if(!vis[e[i].v] && e[i].v!=fa){
     25         getRoot(e[i].v,u);
     26         siz[u]+=siz[e[i].v];
     27         mxson=max(mxson,siz[e[i].v]);
     28     }
     29     mxson=max(mxson,Siz-siz[u]);
     30     if(mxson<mn) mn=mxson, root=u;
     31 }
     32 int f[N],mxdis;
     33 void dfs(int u,int fa,int dis){
     34     f[dis]++, mxdis=max(mxdis,dis);
     35     rep_e(i,u) if(!vis[e[i].v] && e[i].v!=fa) dfs(e[i].v,u,dis+1);
     36 }
     37 int r[N<<2];
     38 int getlr(int len){
     39     int tmp=1,_len=0; for(; tmp<=len; ++_len,tmp<<=1); len=_len; //之前程序跑得巨慢是因为多项式长度求错了,这行的tmp<=len写成了tmp<=cnt...... 
     40     rep(i,0,tmp-1) r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
     41     return tmp;
     42 }
     43 int Pow(ll x,int y){
     44     ll res=1;
     45     while(y) {if(y&1) res=res*x%mod; x=x*x%mod; y>>=1;}
     46     return res;
     47 }
     48 void ntt(int *c,int lim,int tag){
     49     int i,j,k;
     50     for(i=0;i^lim;++i) if(i<r[i]) swap(c[i],c[r[i]]);
     51     for(i=1;i<lim;i<<=1){
     52         int wn = Pow(tag==1?G:inv[G], (mod-1)/(i<<1));
     53         for(j=0;j<lim;j+=(i<<1)){
     54             int w=1, x, y;
     55             for(k=0; k^i; ++k,w=(ll)w*wn%mod)
     56                 x=c[j+k], y=(ll)w*c[j+i+k]%mod, c[j+k]=(x+y)%mod, c[j+i+k]=(x-y+mod)%mod;
     57         }
     58     }
     59     if(tag==-1){
     60         int invn=Pow(lim,mod-2);
     61         rep(i,0,lim-1) c[i]=(ll)c[i]*invn%mod;
     62     }
     63 }
     64 
     65 int A[N<<2];
     66 void mul(int len){
     67     int lim=getlr(len<<1);
     68     rep(i,0,len) A[i]=f[i]; rep(i,len+1,lim-1) A[i]=0;
     69     ntt(A,lim,1);
     70     rep(i,0,lim-1) A[i]=(ll)A[i]*A[i]%mod;
     71     ntt(A,lim,-1);
     72     return;
     73 }
     74 int sum[N];
     75 void getAns(int u,int val,int dep){
     76     mxdis=0, dfs(u,u,dep), mul(mxdis);
     77     rep(i,0,mxdis<<1) sum[i+1]=(sum[i+1]+(ll)A[i]*val)%mod;
     78     rep(i,0,mxdis) f[i]=0;
     79     return;
     80 }    
     81 void calc(int u){//inclusion-exclusion
     82     getAns(u,1,0);
     83     rep_e(i,u) if(!vis[e[i].v]) getAns(e[i].v,-1,1);
     84 }
     85 void solve(int u,int s){
     86     if(s==1){sum[1]=(sum[1]+1)%mod; vis[u]=1; return;}
     87     Siz=s, mn=2147483647, getRoot(u,u);
     88     vis[u=root]=1, calc(u);
     89     rep_e(i,u) if(!vis[e[i].v]){
     90         solve(e[i].v, siz[e[i].v]>siz[u]?s-siz[u]:siz[e[i].v]);
     91     }
     92 }
     93 int ans;
     94 int main(){
     95     n=read();
     96     int u,v;
     97     rep(i,2,n) u=read(), v=read(), add(u,v), add(v,u);
     98     inv[1]=1; rep(i,2,n) inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
     99     solve(1,n);
    100     rep(i,1,n) ans=(ans+(ll)inv[i]*sum[i]%mod)%mod;
    101     printf("%d
    ",ans);
    102     return 0;
    103 }
    View Code

    T3

    常规连边是不可能的,我们回去考虑朴素做法。

    发现题目的加法操作就是在邻接矩阵的两个矩形区域集体加一个数。

    可以用扫描线加线段树维护这个。

    但是怎么合并连通块?

    有一种冷门的最小生成树算法(王爷以前提过)叫 $boruvka$算法。

    做法是对于当前每个连通块,找到这块与其它块的连边中权值最小的那条,把它连上。

    这样做一轮,连通块数量至少减少一半,所以只需要做 $log(n)$ 轮。

    由于正常情况下这个算法的复杂度也是 $O(n imes log(n))$ 的,没 $kruskal$ 算法直观,所以几乎不用。

    但在这题里用这种做法来连边效果会很好。

    我们把每个矩形拆成 $2$ 个扫描线上的操作。

    线段树上每个节点维护 $2$ 个 $pair$,记录连向两个不同连通块的边所指向的连通块和这条边的边权,在指向不同连通块的基础上再使边权最小。

    线段树 $pushup$ 时讨论一下即可转移。

    最后 $2$ 条边中至少有一条指向与当前点不在同一连通块的点,连上这条边即可。


    我讲个道理,没有文字题解真的不是什么好事情,因为文字可以解释代码,而代码未必能很好地解释文字。做过一道题,过了若干个月就忘了怎么做的事情已经发生很多次了,相信我不用列举的。

  • 相关阅读:
    LintCode Python 简单级题目 41.最大子数组
    helm深入学习
    kubernetes组件helm
    解压war包
    linux打开进程数测试
    docker使用centos7系统构建oraclejdk镜像
    docker使用centos7系统构建tomcat镜像
    docker使用alpine系统构建tomcat镜像
    docker制作共享jdk的tomcat镜像
    java cpu使用率高异常排查
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/2019_3_20.html
Copyright © 2011-2022 走看看