zoukankan      html  css  js  c++  java
  • SDU暑期集训排位(7)

    C - Toll Management IV

     我要气死了!下次我不自己看输入输出我是狗!对于树边的$a_i$,考虑非树边对他的贡献,从小到大将非树边$(u,v)$排序,他能贡献的是(u->lca)和(v->lca),缩树或者是树链剖分都可以。对于非树边的$b_i$,倍增求最大即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>P;
    const int maxn=1e5+5;
    int n, m, a[maxn], par[maxn], id[maxn], b[maxn];
    int p[maxn][18], mx[maxn][18];
    vector<P>g[maxn];
    struct node{
        int from, to, cost, id;
        node(int from=0, int to=0, int cost=0, int id=0):from(from),to(to),cost(cost),id(id){}
        bool operator<(const node& g)const{
            return cost<g.cost;
        }
    }edge[maxn], edge1[maxn];
    int T;
    bool f[maxn];
    int fd(int x){return x==par[x]?x:par[x]=fd(par[x]);}
    void unite(int x, int y){
        x=fd(x); y=fd(y);
        if(x==y) return;
        par[x]=y;
    }
    int fat[maxn], depth[maxn];
    void dfs(int u, int fa, int d){
        fat[u]=fa; depth[u]=d;
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].first; if(v==fa) continue;
            dfs(v,u,d+1); id[v]=g[u][i].second; mx[v][0]=edge[g[u][i].second].cost;
        }
    }
    void init(){
        for(int i=1;i<=n;i++) if(fat[i]!=i) p[i][0]=fat[i];
        for(int j=1;j<=15;j++){
            for(int i=1;i<=n;i++){
                if(p[i][j-1]!=-1) p[i][j]=p[p[i][j-1]][j-1], mx[i][j]=max(mx[i][j-1],mx[p[i][j-1]][j-1]);
            }
        }
    }
    int query(int u, int v){
        if(depth[u]<depth[v]) swap(u,v);
        int t=depth[u]-depth[v]; int ans=0;
        for(int i=0;i<=15;i++) if(t&(1<<i)) ans=max(ans,mx[u][i]), u=p[u][i];
        if(u==v) return ans;
        for(int i=15;i>=0;i--){
            if(p[u][i]!=p[v][i]) ans=max(ans,mx[u][i]), ans=max(ans,mx[v][i]), u=p[u][i], v=p[v][i];
        }
        ans=max(ans,mx[u][0]); ans=max(ans,mx[v][0]);
        return ans;
    }
    int main(){
        scanf("%d",&T); int cas=0;
        while(T--){
            scanf("%d%d",&n,&m); int u, v, w;
            for(int i=1;i<=n;i++) g[i].clear(), par[i]=i;
            for(int i=1;i<=m;i++) scanf("%d%d%d",&u,&v,&w), edge[i]=node(u,v,w,i), a[i]=b[i]=-1;
            for(int i=1;i<=n-1;i++){
                u=edge[i].from; v=edge[i].to; w=edge[i].cost;
                g[u].push_back(P(v,i)); g[v].push_back(P(u,i));
            }
            memset(p,-1,sizeof(p)); memset(mx,0,sizeof(mx));
            dfs(1,1,1); init();
            sort(edge+n,edge+m+1);
            for(int i=n;i<=m;i++){
                u=edge[i].from; v=edge[i].to; w=edge[i].cost;
                int j=edge[i].id;
                b[j]=w-query(u,v);
                u=fd(u); v=fd(v);
                int tu=u, tv=v;
                while(u!=v){
                    if(depth[u]>depth[v]) u=fd(fat[u]);
                    else v=fd(fat[v]);
                }
                int t=u; u=tu, v=tv;
                while(u!=t) a[id[u]]=w-edge[id[u]].cost, unite(u,t), u=fd(fat[u]);
                while(v!=t) a[id[v]]=w-edge[id[v]].cost, unite(v,t), v=fd(fat[v]);
            }
            ll ans=0;
            for(int i=1;i<=m;i++){
                int k=edge[i].id;
                if(k<n) ans+=1LL*k*a[k]-1LL*k*k;
                else ans+=1LL*k*k*b[k]-k;
            }
            printf("Case %d: %lld
    ",++cas,ans);
        }
        return 0;
    }
    View Code

    F - Unique Party

    暴力。首先,我们可以进行一些常规操作,压缩矩形,即枚举矩形的上下界,然后再考虑左右边界。对于一次询问,实际上我们可以将矩阵变换一下,将大于或等于$h$的位置替换成$1$,将小于$h$位置替换为$-1$,然后问题就变成了求最大的子矩阵使得矩阵的和不小于零,然后加上之前的枚举上下界,实际上,就变成了一个一维问题:给定一个整数序列,求最长的子区间,使得区间和不小于零。对于此问题,我们考虑枚举左边界,注意都这没有单调性,故我们不能尺取来优化,但是如果用一些数据结构维护的话,可能会超时,所以这个问题我们必须做到线性复杂度。实际上,虽然每个左端点的最优的右端点是无单调性的,但是,假如左端点$l$对应的最优右端点为$r$,那么对于$l+1$来讲,它的右端点不能小于$r+1$,因此,如果小的话,答案只会更差,所以说,实际上我们的右端点还有可以选择不断右移的,但是存在一个问题,我们如何快速判断当前的右端点是否是最优解呢?我们可以假设$r$不是当前$l$的最优解,那么从$r+1$开始,必然存在连续的一段子区间,它的和加上$[l,r]$的和不小于零,因此,我们不妨对每个点维护一个$suf[i]$,表示从$i$位置往后,包括$i$的最大连续子段和,显然,如果$r$不是最优解,那么$[l,r]$区间的和加上$suf[r+1]$是不小于零的,而$suf[i]$可以线性求,有了$suf[i]$,我们前一个问题也可以线性求,所以这个问题就变成了线性时间复杂度了。最后加上询问次数以及枚举上下界,复杂度为$O(qn^3)$。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 int T,R,C;
     5 const int N=260;
     6 int a[N][N],b[N],suf[N];
     7 int main() {
     8     scanf("%d",&T);
     9     int kase=0,q;
    10     while(T--) {
    11         scanf("%d%d",&R,&C);
    12         for(int i=1;i<=R;i++) for(int j=1;j<=C;j++) scanf("%d",a[i]+j);
    13         scanf("%d",&q);
    14         printf("Case %d:
    ",++kase);
    15         while(q--) {
    16             int h,ans=0;;
    17             scanf("%d",&h);
    18             for(int l=1;l<=R;l++) {
    19                 for(int j=1;j<=C;j++) b[j]=0;
    20                 for(int r=l;r<=R;r++) {
    21                     for(int j=1;j<=C;j++) {
    22                         if(a[r][j]>=h) b[j]++;
    23                         else b[j]--;
    24                     }
    25                     suf[C+1]=0;
    26                     for(int i=C;i;i--) {
    27                         suf[i]=b[i];
    28                         suf[i]+=std::max(0,suf[i+1]);
    29                     }
    30                     int p=0,now=0;;
    31                     for(int i=1;i<=C;i++) {
    32                         p=std::max(p,i-1);
    33                         if(p<i) now=0;
    34                         while(p<C&&now+suf[p+1]>=0) {
    35                             p++;
    36                             now+=b[p];
    37                         }
    38                         if(now>=0) ans=std::max(ans,(r-l+1)*(p-i+1));
    39                         if(p>=i) now-=b[i];
    40                     }
    41                 }
    42             }
    43             printf("%d
    ",ans);
    44         }
    45     }
    46     return 0;
    47 }
    View Code

    H - Design New Capital

    $NTT$。首先观察到,点不会在坐标轴上,$(0,0)$能成为最优点的条件就是,$x$轴上方的点的个数等于下方,$y$轴左边的点数等于右边,从而得到第一象限的点的个数和第三象限相等,第二和第四相等。不妨对一三象限和二四象限分开考虑,设第$i$象限的点数为$f[i]$,那么取$k$个点的方案数$ans[k]=sum_{i=0}^{frac{k}{2}}C(f[1],i)C(f[3],i)C(f[2],k-i)C(f[4],k-i)$,当$k$为奇数时显然方案数为$0$,偶数的话,根据式子可以发现,它实际上是个卷积,所以$FFT$优化一下,题目比较友好,给了个$NTT$模数,且打表可以发现,原根为$3$,所以可以使用$NTT$。

     1 #include<iostream>
     2 #include<cstdio>
     3 const int N=1e5+5;
     4 typedef long long ll;
     5 const ll mod=7340033,ot=3;
     6 int T;
     7 ll qpow(ll x,ll y) {
     8     ll res=1;
     9     x%=mod;y%=(mod-1);
    10     if(y<0) y+=mod-1;
    11     for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
    12     return res;
    13 }
    14 ll p[N],inv[N];
    15 void init() {
    16     p[0]=1;
    17     for(int i=1;i<N;i++) p[i]=p[i-1]*i%mod;
    18     inv[N-1]=qpow(p[N-1],mod-2);
    19     for(int i=N-2;~i;i--) inv[i]=inv[i+1]*(i+1)%mod;
    20 }
    21 ll C(int n,int m) {
    22     if(n<m) return 0;
    23     return p[n]*inv[m]%mod*inv[n-m]%mod;
    24 }
    25 void NTT(ll *a,int n,bool inv=0) {
    26     for(int i=0,j=0;i<n;i++) {
    27         if(j>i) std::swap(a[i],a[j]);
    28         int k=n;
    29         while(j&(k>>=1)) j&=~k;
    30         j|=k;
    31     }
    32     ll p=inv?-(mod-1)/2:(mod-1)/2;
    33     for(int i=1;i<n;i<<=1) {
    34         ll c=qpow(ot,p/i),o=1;
    35         for(int k=0;k<i;k++,o=o*c%mod) {
    36             for(int j=k;j<n;j+=i<<1) {
    37                 int e=j+i;
    38                 ll t=o*a[e]%mod;
    39                 a[e]=(a[j]-t+mod)%mod;
    40                 a[j]=(a[j]+t)%mod;
    41             }
    42         }
    43     }
    44     if(inv) {
    45         p=qpow(n,mod-2);
    46         for(int i=0;i<n;i++) a[i]=a[i]*p%mod;
    47     }
    48 }
    49 void mul(ll *a,ll *b,int la,int lb) {
    50     int n=1;while(n<la+lb-1) n<<=1;
    51     for(int i=la;i<n;i++) a[i]=0;
    52     for(int i=lb;i<n;i++) b[i]=0;
    53     NTT(a,n);NTT(b,n);
    54     for(int i=0;i<n;i++) a[i]=a[i]*b[i]%mod;
    55     NTT(a,n,true);
    56 }
    57 ll a[2][N<<3];
    58 int num[4],n;
    59 int main() {
    60     scanf("%d",&T);
    61     init();int kase=0;
    62     while(T--) {
    63         scanf("%d",&n);
    64         for(int j=0;j<4;j++) num[j]=0;
    65         for(int i=0,x,y;i<n;i++) {
    66             scanf("%d%d",&x,&y);
    67             if(x>0&&y>0) num[0]++;
    68             else if(x<0&&y>0) num[1]++;
    69             else if(x<0&&y<0) num[2]++;
    70             else num[3]++;
    71         }
    72         int m[2]={0,0};
    73         for(int i=0;i<2;i++) {
    74             int up=std::min(num[i],num[i+2]);
    75             for(int j=0;j<=up+up;j++) a[i][j]=0;
    76             m[i]=up+up+1;
    77             for(int j=0;j<=up;j++) a[i][j+j]=C(num[i],j)*C(num[i+2],j)%mod;
    78         }
    79         mul(a[0],a[1],m[0],m[1]);
    80         printf("Case %d:
    ",++kase);
    81         for(int i=1;i<=n;i++) printf("%lld%c",a[0][i]," 
    "[i==n]);
    82     }
    83     return 0;
    84 }
    View Code

    I - Numbered Cards

    $dp$。首先,我们使用$bitmask$来表示每个数位是否出现过,如果$mask$第$i$位不为0,它就出现过。然后我们用$f[mask]$表示数位出现情况为$mask$的数字有多少个,这个可以通过数位$dp$求解。然后我们再用$dp[mask]$表示数位出现情况为$mask$的选取数的方案数,显然,如果我们只选一个数,那么就有方案数$f[mask]$,否则的话,我们考虑最小的那个数位有多少种选择,也就是考虑我们选出的包含最小数位的那个数还包含哪些数位,假设它的数位情况为$s$,则有$dp[mask]=f[s] imes dp[s oplus mask]$,我们枚举所有情况转移即可,复杂度$O(3^{10})$。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 const int N=11,M=1<<10;
     5 int dp[N][M];
     6 typedef long long ll;
     7 ll f[M];
     8 const ll mod=1e9+7;
     9 int now,a[N],num[M],top;
    10 int dfs(int u,int mask,bool limit,bool zero) {
    11     if(u<0) return now==mask?1:0;
    12     if(!limit&&!zero&&dp[u][mask]!=-1) return dp[u][mask];
    13     int ans=0;
    14     int up=limit?a[u]:9;
    15     for(int i=0;i<=up;i++) {
    16         int to=mask;
    17         if(i) to|=1<<i;
    18         else if(!zero) to|=1;
    19         if((to|now)==now) ans+=dfs(u-1,to,limit&&i==up,zero&&!i);
    20     }
    21     if(!limit&&!zero) dp[u][mask]=ans;
    22     return ans;
    23 }
    24 void solve(int n) {
    25     top=0;
    26     while(n) {
    27         a[top++]=n%10;
    28         n/=10;
    29     }
    30 }
    31 int main() {
    32     int kase=0,T;
    33     scanf("%d",&T);
    34     while(T--) {
    35         int n;
    36         scanf("%d",&n);
    37         for(int i=0;i<M;i++) f[i]=0,num[i]=0;
    38         solve(n);
    39         ll ans=0;
    40         for(int i=2;i<M;i++) {
    41             now=i;
    42             memset(dp,-1,sizeof(dp));
    43             num[i]=dfs(top-1,0,true,true);
    44             f[i]=num[i];
    45             int lowbit=i&-i;
    46             for(int s=(i-1)&i;s;s=(s-1)&i) {
    47                 if(s<2) continue;
    48                 if((s^i)<2) continue;
    49                 if(!(s&lowbit)) continue;
    50                 f[i]=(f[i]+num[s]*f[s^i])%mod;
    51             }
    52             ans=(ans+f[i])%mod;
    53 
    54         }
    55         printf("Case %d: %lld
    ",++kase,ans);
    56     }
    57     return 0;
    58 }
    View Code
  • 相关阅读:
    在React中使用Redux数据流
    开发中经常遇到的一些css样式问题
    记录一下CSS outline-width 属性
    使用git stash命令保存和恢复进度
    一分钟搭建好webpack通用坏境
    二维码生成(QRCode.js)
    IE6浏览器有哪些常见的bug,缺陷或者与标准不一致的地方,如何解决
    如何进行网站性能优化
    JavaScript深拷贝与浅拷贝的理解
    新问题: 两个样式对同一个元素操作,为什么最新的样式没有起作用?(已解决)
  • 原文地址:https://www.cnblogs.com/Onlymyheart/p/11373144.html
Copyright © 2011-2022 走看看