2019 ICPC Asia Nanchang Regional(C,E,G,K,L)

C. And and Pair(Dp)
要使得x&n=x,那么就只有n二进制表示为1的位能使得x在这一位为1,同时x<=n的条件也满足了。

设dp[i][0]表示到第i位,x还没过1的方案数,dp[i][1]表示到第i位x放过1的方案数。
然后根据第i位的字符进行转移即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
typedef long long ll;
const int mod=1e9+7;
char s[N];
int n;
ll dp[N][2];
int main()
{
    
    
    int t;scanf("%d",&t);
    while(t--)
    {
    
    
        scanf("%s",s+1);
        n=strlen(s+1);
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(int i=1;i<=n;i++)
        {
    
    
            if(s[i]=='1')
            {
    
    
                dp[i][1]=(dp[i][1]+dp[i-1][1]*3+dp[i-1][0])%mod;
            }
            else
            {
    
    
                dp[i][1]=(dp[i][1]+dp[i-1][1]*2)%mod;
            }
            dp[i][0]=dp[i-1][0];
        }
        printf("%lld\n",(dp[n][0]+dp[n][1])%mod);
    }
}

E. Bob’s Problem(Kruskal最大生成树)
把黑边全部加入,白边按权值排序做一颗最大生成树即可,判断连完后是否是一整个连通块。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
typedef long long ll;
int n,m,k,f[N];
struct node
{
    
    
    int u,v,w;
    node(int u=0,int v=0,int w=0):u(u),v(v),w(w){
    
    }
    bool operator<(const node&o)const
    {
    
    
        return w>o.w;
    }
};
vector<node>v[2];
int getf(int x){
    
    return f[x]==x?x:f[x]=getf(f[x]);}
int main()
{
    
    
    int t;scanf("%d",&t);
    while(t--)
    {
    
    
        v[0].clear();v[1].clear();
        scanf("%d%d%d",&n,&m,&k);
        assert(m<=500000);assert(k<=500000);
        assert(n<=50000);
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=m;i++)
        {
    
    
            int x,y,w,p;scanf("%d%d%d%d",&x,&y,&w,&p);
            v[p].push_back(node(x,y,w));
        }
        ll ans=0;
        int cnt=0;
        for(int i=0;i<v[0].size();i++)
        {
    
    
            ans+=v[0][i].w;
            int fu=getf(v[0][i].u),fv=getf(v[0][i].v);
            if(fu!=fv) f[fu]=fv,cnt++;
        }
        priority_queue<int>q;
        sort(v[1].begin(),v[1].end());
        for(int i=0;i<v[1].size()&&k;i++)
        {
    
    
            int fu=getf(v[1][i].u),fv=getf(v[1][i].v);
            if(fu==fv)
            {
    
    
                q.push(v[1][i].w);continue;
            }
            cnt++;
            k--;ans+=v[1][i].w;f[fu]=fv;
        }
        if(cnt!=n-1) {
    
    printf("-1\n");continue;}
        while(k&&!q.empty())
        {
    
    
            ans+=q.top();q.pop();k--;
        }
        printf("%lld\n",ans);
    }
}

G. Eating Plan
此题看起来不好做,但是看到ai是n的一个排列,并且模数的特殊性,可以猜想到当a!大到一定程度一定会为0,否则此题根本不可解。

然后本地打表可以发现只有大概前3000个以下的数的阶乘不为0,其它的都为0,因为0对取模没有影响。

于是O(3000*3000)枚举所有左右端点求出长度为i,能获得的最大模数ki,然后把查询排序遍历一遍即可。

#include<bits/stdc++.h>
using namespace std;
const int p=2803,mod=998857459,N=1e5+5;
typedef long long ll;
int n,m,a[N],s[N],ans[N];
ll f[N],sum[N],ff[N];
vector<int>v;
struct node
{
    
    
    int x,id;
    bool operator<(const node&o)const
    {
    
    
        return x<o.x;
    }
}q[N];
int main()
{
    
    
    memset(ans,-1,sizeof(ans));
    f[0]=f[1]=1;
    for(int i=2;i<N;i++) f[i]=f[i-1]*i%mod;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%d",&a[i]);
        a[i]=f[a[i]];
        ff[1]=max(ff[1],(ll)a[i]);
        if(a[i]!=0) v.push_back(i);
        sum[i]=(sum[i-1]+a[i])%mod;
    }
    for(int i=0;i<v.size();i++)
        for(int j=i;j<v.size();j++)
    {
    
    
        ff[v[j]-v[i]+1]=max(ff[v[j]-v[i]+1],(sum[v[j]]-sum[v[i]-1]+mod)%mod);
    }
    for(int i=1;i<=m;i++)
        scanf("%d",&q[i].x),q[i].id=i;
    sort(q+1,q+1+m);
    int j=1;
    for(int i=1;i<=n;i++)
    {
    
    
        while(j<=m&&q[j].x<=ff[i])
            ans[q[j].id]=i,j++;
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}

K. Tree(哈希+线段树+启发式合并)
题意:找出有序对(u,v)的个数,满足以下条件:
u!=v
u,v互相不为对方的祖先
u,v的距离<=k
lca(u,v)的权值乘2==u,

我是真的看不懂up to k是<=k的意思。。。原谅我英语水平太菜。

考虑枚举lca,给lca为u时,每次加入一个子树,给不同的权值开一个线段树的根,每棵线段树保存权值为a[x],不同深度的点的数量。

用树上启发式合并可以保证复杂度。

但是再考虑到线段树的开销问题,其实只要开n个线段树的根就可以了。

再合并的时候把点v的子树合并到lca的子树中时,如果lca中不包含点v中权值一样的根,直接把点v中的根copy进去并且树的形态不需要改变。

这里用哈希表就可以做到。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=5000011;
ll ans;
int rt[N],num,ls[N*40],rs[N*40],sum[N*40];
int n,mx,k,a[N],dep[N],f[N],tot,head[N],nex[N],to[N];
void add(int u,int v){
    
    to[++tot]=v;nex[tot]=head[u];head[u]=tot;}
int query(int l,int r,int x,int now)
{
    
    
    if(!now||l>x) return 0;
    if(r<=x) return sum[now];
    int m=l+r>>1;
    return query(l,m,x,ls[now])+query(m+1,r,x,rs[now]);
}
void fix(int l,int r,int x,int &now)
{
    
    
    if(!now) now=++num;
    sum[now]++;
    if(l==r) return;
    int m=l+r>>1;
    if(x<=m) fix(l,m,x,ls[now]);
    else fix(m+1,r,x,rs[now]);
}
struct hs_table
{
    
    
    int tot,num,head[M],nex[M],f[M];
    ll to[M];
    hs_table(){
    
    memset(head,0,sizeof head);tot=num=0;}
    int ins(ll x,int s)
    {
    
    
        int t=x%M;
        for(int i=head[t];i;i=nex[i])
            if(to[i]==x) return f[i];
        to[++tot]=x;nex[tot]=head[t];head[t]=tot;
        f[tot]=s==-1?++num:s;
        return f[tot];
    }
    int query(ll x)
    {
    
    
        int t=x%M;
        for(int i=head[t];i;i=nex[i])
            if(to[i]==x) return f[i];
        return -1;
    }
}hs;
vector<int>vc[N];
void dfs(int u,int p)
{
    
    
    dep[u]=dep[p]+1;
    mx=max(mx,dep[u]);
    for(int i=head[u];i;i=nex[i])
        dfs(to[i],u);
}
bool vis[N];
int top,st[N];
void dfs(int u)
{
    
    
    ll fu,fv;
    for(int i=head[u];i;i=nex[i])
    {
    
    
        int v=to[i];
        dfs(v);
        fv=f[v];fu=f[u];
        if(vc[fv].size()<vc[fu].size())
        {
    
    
            for(int j=0;j<vc[fv].size();j++)
            {
    
    
                int x=vc[fv][j],s=hs.query(fu*1000000000+2*a[u]-a[x]);;
                if(dep[x]-dep[u]>=k||s==-1) continue;
                ans+=query(1,mx,k-dep[x]+2*dep[u],rt[s]);
            }
            for(int j=0;j<vc[fv].size();j++)
            {
    
    
                vc[fu].push_back(vc[fv][j]);
                if(vis[a[vc[fv][j]]]) continue;
                int x=vc[fv][j],s=hs.query(fu*1000000000+a[x]);
                if(s==-1)
                    hs.ins(fu*1000000000+a[x],hs.query(fv*1000000000+a[x])),vis[a[x]]=true,st[++top]=a[x];
                else fix(1,mx,dep[x],rt[s]);
            }
        }
        else
        {
    
    
            for(int j=0;j<vc[fu].size();j++)
            {
    
    
                int x=vc[fu][j],s=hs.query(fv*1000000000+2*a[u]-a[x]);;
                if(dep[x]-dep[u]>=k||s==-1) continue;
                ans+=query(1,mx,k-dep[x]+2*dep[u],rt[s]);
            }
            for(int j=0;j<vc[fu].size();j++)
            {
    
    
                vc[fv].push_back(vc[fu][j]);
                if(vis[a[vc[fu][j]]]) continue;
                int x=vc[fu][j],s=hs.query((ll)fv*1000000000+a[x]);
                if(s==-1)
                    hs.ins(fv*1000000000+a[x],hs.query(fu*1000000000+a[x])),vis[a[x]]=true,st[++top]=a[x];
                else fix(1,mx,dep[x],rt[s]);
            }
            f[u]=f[v];
        }
        for(int i=1;i<=top;i++) vis[st[i]]=false;
        top=0;
    }
    fu=f[u];
    int s=hs.ins(fu*1000000000+a[u],-1);
    fix(1,mx,dep[u],rt[s]);
    vc[fu].push_back(u);
}
int main()
{
    
    
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=i;
    for(int i=2;i<=n;i++)
    {
    
    
        int x;scanf("%d",&x);
        add(x,i);
    }
    dfs(1,0);
    dfs(1);
    printf("%lld\n",ans*2);
}

L. Who is the Champion
题意:有n个球队,给出n*n的矩阵第i行第j列表示第i个球队和第j个球队踢球进球的个数。两个球队pk,赢的球队加3分,输的球队不加分。平局各加一分。规定分数最多的球队为冠军,如果有多个分数最多的,按每个队赢球数-失球数最高的,如果仍有多个,输出play-offs。否则输出第几个球队是冠军。

本场的签到题,读懂题意模拟即可。

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,a[N][N],b[N],c[N];
struct node
{
    
    
    int x,f,s;
    node(int x=0,int f=0,int s=0):x(x),f(f),s(s){
    
    }
    bool operator<(const node&o)const
    {
    
    
        return f==o.f?s>o.s:f>o.f;
    }
}p[N];
int main()
{
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
    {
    
    
        if(i==j) continue;
        c[i]+=a[i][j];
        if(a[i][j]>a[j][i]) b[i]+=3;
        else if(a[i][j]==a[j][i]) b[i]++;
        c[i]-=a[j][i];
    }
    for(int i=1;i<=n;i++)
        p[i]=node(i,b[i],c[i]);
    sort(p+1,p+1+n);
    if(n==1) printf("1\n");
    else
    {
    
    
        if(p[1].s==p[2].s&&p[1].f==p[2].f) printf("play-offs\n");
        else printf("%d\n",p[1].x);
    }
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/103906246