BCBestCoder Round #74

链接:BestCoder Round #74

A:给定n,接下来是n-1个整数a[i]。a[i]表示在字符串s中从第i个开始的子串和从第i+1个开始的子串的最长公共前缀为a[i]。求字符串s有多少种情况。

分析:首先我们处理无解的情况,显然a[i]+i>n是无解的,并且相邻的a[i]如果非0则必然为a[i]-1==a[i+1]。然后就只要算方案数了。O(n)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
int a[N];
int main()
{
    int i,n,t;
    ll ans;
    scanf("%d", &t);
    while (t--) {
        a[0]=0;ans=1;
        scanf("%d", &n);
        for (i=1;i<n;i++) scanf("%d", &a[i]);
        for (i=1;i<n;i++)
        if ((a[i-1]&&a[i]!=a[i-1]-1)||(i+a[i]>n)) ans=0;
        if (ans==0) { printf("0\n");continue ; }
        ans=26;
        for (i=2;i<n;i++)
        if (!a[i-1]) ans=ans*25%MOD;
        if (!a[n-1]) ans=ans*25%MOD;
        printf("%I64d\n", (ans+MOD)%MOD);
    }
    return 0;
}

B:给定长度n,点i与点i+1的距离为1。然后加3条边长为1的边{a[1],b[1]},{a[2],b[2]},{a[3],b[3]}。然后给出m个询问,每次询问u到v的最短路径是多少。

分析:我们把这3条新加的边看做是一种快速通道,那么每一条通道都可以选择用与不用和正反使用。那么我们暴力dfs搜出27情况,并且3条边的先后是可以交换的,我们再对6种排列的都做一次。O(162*m)。。这样被标程的O(36*m)卡了。。我们加点剪枝:因为我们暴力将所有的情况:一条边的正反使用都搜了,很显然正反中必然只有一种是优的,另一种肯定会差一些,那么我们在当前dis已经大于min的时候直接return。然后就过了。。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
int a[4],b[4],x[4],y[4],mi;
void dfs(int z,int x,int y,int dis) {
    if (dis>=mi) return ;
    if (z==4) {
        mi=min(mi,dis+abs(x-y));return ;
    }
    dfs(z+1,x,y,dis);
    dfs(z+1,b[z],y,dis+abs(x-a[z])+1);
    dfs(z+1,a[z],y,dis+abs(x-b[z])+1);
}
void change(int z) {
    if (z==1) { a[1]=x[1];a[2]=x[2];a[3]=x[3];b[1]=y[1];b[2]=y[2];b[3]=y[3]; }
    if (z==2) { a[1]=x[1];a[2]=x[3];a[3]=x[2];b[1]=y[1];b[2]=y[3];b[3]=y[2]; }
    if (z==3) { a[1]=x[2];a[2]=x[1];a[3]=x[3];b[1]=y[2];b[2]=y[1];b[3]=y[3]; }
    if (z==4) { a[1]=x[2];a[2]=x[3];a[3]=x[1];b[1]=y[2];b[2]=y[3];b[3]=y[1]; }
    if (z==5) { a[1]=x[3];a[2]=x[1];a[3]=x[2];b[1]=y[3];b[2]=y[1];b[3]=y[2]; }
    if (z==6) { a[1]=x[3];a[2]=x[2];a[3]=x[1];b[1]=y[3];b[2]=y[2];b[3]=y[1]; }
}
int main()
{
    int i,j,t,n,m,e,f;
    ll ans;
    scanf("%d", &t);
    while (t--) {
        ans=0;
        scanf("%d%d", &n, &m);
        scanf("%d%d%d%d%d%d", &x[1], &y[1], &x[2], &y[2], &x[3], &y[3]);
        for (i=1;i<=m;i++) {
            scanf("%d%d", &e, &f);
            mi=1000000000;
            for (j=1;j<7;j++) {
                change(j);dfs(1,e,f,0);
            }
            ans=(ans+(ll)mi*i)%MOD;
        }
        printf("%I64d\n", (ans+MOD)%MOD);
    }
    return 0;
}


C:给定n个数a[i]。m组询问,每次询问s,t:s最少要通过多少次操作变成t。有两种操作:(1)将x的二进制的某一位取反。(2)将x与一个给定的a[i]异或。

分析:首先我们分析下这两种操作,初一看异或和取反不好操作,但是仔细一看会发现我们可以将第一种操作变成第二种操作:将x的第i位取反相当于与(1<<(i-1))异或。那么我们就变成了(s^x1^x2...^xk=t)==>(x1^x2^...^xk=s^t),我们发现所有数的范围在10^5以内,并且能用来异或的数xi最多只有35个,那么我们先bfs对所有的1~10^5都处理出最小所需的操作数,然后对于每一次询问直接给出答案即可。O(10^5)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=500010;
const int MAX=151;
const int MOD1=1000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
const ll INF=10000000010;
typedef unsigned long long ull;
int a[40],q[N],dp[N];
queue<int>Q;
int main()
{
    int i,t,n,m,x,y,w;
    ll ans;
    scanf("%d", &t);
    while (t--) {
        ans=0;
        scanf("%d%d", &n, &m);
        for (i=1;i<=n;i++) scanf("%d", &a[i]);
        for (i=1;i<=20;i++) a[n+i]=1<<(i-1);
        while (!Q.empty()) Q.empty();
        memset(q,0,sizeof(q));
        Q.push(0);q[0]=1;dp[0]=0;n+=20;
        while (!Q.empty()) {
            w=Q.front();Q.pop();
            for (i=1;i<=n;i++)
            if (w^a[i]<=100000&&a[i]<=100000) {
                if (q[w^a[i]]) continue ;
                q[w^a[i]]=1;
                dp[w^a[i]]=dp[w]+1;
                Q.push(w^a[i]);
            }
        }
        for (i=1;i<=m;i++) {
            scanf("%d%d", &x, &y);
            ans=(ans+(ll)dp[x^y]*i)%MOD;
        }
        printf("%I64d\n", (ans+MOD)%MOD);
    }
    return 0;
}

D:给定一个n个点m条边的DAG图和一个k。求最多能删k条边产生的字典序最小的拓扑序是多少。

分析:因为要产生字典序最小,那么很显然我们要贪心取当前剩下的最小点,如果它的入度<=k的话,那么就把它的入度全删掉。每次我们都要找到入度<=k的编号最小的剩下的点,用线段树或优先队列维护一下就好了。O((n+m)*logn)

代码:

优先队列:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
int tot,u[N],v[2*N],pre[2*N];
void add(int a,int b) {
    v[tot]=b;pre[tot]=u[a];u[a]=tot++;
}
int d[N],q[N],vis[N];
priority_queue<int, vector<int>, greater<int> >Q;
int main()
{
    int a,b,i,g,t,n,m,k,w;
    ll ans;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d", &n, &m, &k);
        memset(d,0,sizeof(d));
        tot=0;memset(u,-1,sizeof(u));
        for (i=1;i<=m;i++) {
            scanf("%d%d", &a, &b);add(a,b);d[b]++;
        }
        while (!Q.empty()) Q.pop();
        ans=0;g=1;
        memset(q,0,sizeof(q));
        memset(vis,0,sizeof(vis));
        for (i=1;i<=n;i++)
        if (d[i]<=k) { Q.push(i);q[i]=1; }
        while (!Q.empty()) {
            w=Q.top();Q.pop();q[w]=0;
            if (d[w]<=k&&!vis[w]) {
                ans=(ans+(ll)w*g)%MOD;
                vis[w]=1;g++;k-=d[w];
                printf("%d  xx\n", w);
                for (i=u[w];i!=-1;i=pre[i]) {
                    d[v[i]]--;
                    if (vis[v[i]]) continue ;
                    if (d[v[i]]<=k&&!q[v[i]]) Q.push(v[i]);
                }
            }
        }
        printf("%I64d\n", (ans+MOD)%MOD);
    }
    return 0;
}

线段树:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const int INF=100000010;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000007;
typedef unsigned long long ull;
int tot,u[N],v[2*N],pre[2*N];
void add(int a,int b) {
    v[tot]=b;pre[tot]=u[a];u[a]=tot++;
}
int d[N],t[4*N];
void build_tree(int a,int l,int r) {
    if (l+1==r) {
        t[a]=d[l];return ;
    }
    int mid=(l+r)/2;
    build_tree(a<<1,l,mid);
    build_tree((a<<1)+1,mid,r);
    t[a]=min(t[a<<1],t[(a<<1)+1]);
}
int query(int a,int l,int r,int k) {
    if (l+1==r) return l;
    int mid=(l+r)/2;
    if (t[a<<1]<=k) return query(a<<1,l,mid,k);
    else return query((a<<1)+1,mid,r,k);
}
void change(int a,int l,int r,int w) {
    if (l+1==r) {
        t[a]=d[w];return ;
    }
    int mid=(l+r)/2;
    if (w<mid) change(a<<1,l,mid,w);
    else change((a<<1)+1,mid,r,w);
    t[a]=min(t[a<<1],t[(a<<1)+1]);
}
int main()
{
    int a,b,i,j,t,n,m,k,w;
    ll ans;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d", &n, &m, &k);
        tot=0;ans=0;
        memset(d,0,sizeof(d));
        memset(u,-1,sizeof(u));
        for (i=1;i<=m;i++) {
            scanf("%d%d", &a, &b);add(a,b);d[b]++;
        }
        build_tree(1,1,n+1);
        for (i=1;i<=n;i++) {
            w=query(1,1,n+1,k);
            k-=d[w];ans=(ans+(ll)i*w)%MOD;
            d[w]=INF;change(1,1,n+1,w);
            for (j=u[w];j!=-1;j=pre[j]) {
                d[v[j]]--;change(1,1,n+1,v[j]);
            }
        }
        printf("%I64d\n", (ans+MOD)%MOD);
    }
    return 0;
}


你可能感兴趣的:(BCBestCoder Round #74)