BestCoder 74

链接:戳这里

A: LCP Array

题意:给定一个字符串s=s1s2s3s4...s5,si代表字符(小写字母),知道任意两个相邻的后缀的最长公共前缀长度为一个整数ai,这个意思就是相邻字符相等的个数为ai。问满足该字符串s的种数。

思路:样例

4

3 2 1

aaaa

  aaa

    aa

      a

显然这样的字符串必须是 aaaa (26字符任意一个) 才能满足条件,对于相邻两个数不为0且相差不为1,的肯定是不存在的

3

0 0

三个字符中  中间的字符不能与两旁的相同  ans=26*25*25

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF (1ll<<60)-1
#define mod 1000000007
using namespace std;
int T,n;
int a[1001000];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        int num=0;
        for(int i=1;i<n;i++) scanf("%d",&a[i]);
        int flag=0;
        ll ans=26;
        a[n]=0;
        for(int i=1;i<n;i++){
            if(a[i]==0) {
                ans*=25;
                ans%=mod;
                continue;
            }
            if(a[i] && a[i]==a[i+1]+1) continue;
            else {
                flag=1;
                break;
            }
        }
        if(flag==1) cout<<0<<endl;
        else printf("%I64d\n",ans);
    }
    return 0;
}


B:Shortest Path

题意:有一条长度为n的链. 节点i和i+1之间有长度为1的边. 现在又新加了3条边, 每条边长度都是1. 给出m个询问, 每次询问两点之间的最短路.

思路:链上两点之间距离,可以是不过加的边答案是两点之间的距离,加的三条边在链上新增加了6个点,每个点都可以是新的通道,那么就在这6个点上跑最短路,计算从从s->t的距离。复杂度O(64*m)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF (1ll<<60)-1
#define mod 1000000007
using namespace std;
int T;
int n,m;
int u[8],c[8][8],dis[8],vis[8];
ll ans;
int solve(){
    for(int i=0;i<8;i++){
        for(int j=0;j<8;j++){
            c[i][j]=fabs(u[i]-u[j]);
        }
    }
    c[0][1]=c[1][0]=1;
    c[2][3]=c[3][2]=1;
    c[4][5]=c[5][4]=1;
    mst(vis,0);
    for(int i=0;i<8;i++) dis[i]=maxn;
    dis[6]=0;
    for(int i=0;i<8;i++){
        int minn=maxn,g=-1;
        for(int j=0;j<8;j++){
            if(!vis[j] && dis[j]<minn){
                minn=dis[j];
                g=j;
            }
        }
        vis[g]=1;
        for(int j=0;j<8;j++){
            if(!vis[j] && dis[j]>dis[g]+c[g][j]){
                dis[j]=dis[g]+c[g][j];
            }
        }
    }
    return dis[7];
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        ans=0;
        for(int i=0;i<3;i++) scanf("%d%d",&u[i<<1],&u[i<<1|1]);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u[6],&u[7]);
            ans+=((ll)i*solve()%mod+mod)%mod;
            ans%=mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

C: Transform

题意:两个操作,一是将x二进制上的01翻转一次变成另外一个数,而是x抑或一个数y变成另外一个数,给N个y提供选择,问最少需要多少次操作才能使S->T

思路:第一个操作其实就是x抑或2的次方,缩成一个操作,x抑或n个数或者抑或2的次方求操作数最少。

由于S^a1^a2^...an=T  ==>  a1^a2^...an=S^T  抑或满足交换律; 接下来就是从0开始,从(2的次方,a[1~n])中挑选数抑或最后值为S^T, SPFA的思路正好解决了这个问题。复杂度O( (n+17)log(n+17) +m )

代码: 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF (1ll<<60)-1
#define mod 1000000007
using namespace std;
int T,n,m;
int a[55],dis[200100],vis[200100];
queue<int> qu;
void SPFA(){
    mst(vis,0);
    mst(dis,-1);
    vis[0]=1;dis[0]=0;
    qu.push(0);
    while(!qu.empty()){
        int now=qu.front();
        qu.pop();
        vis[now]=0;
        for(int i=0;i<n;i++){
            int u=a[i]^now;
            if(dis[u]!=-1 && dis[u]<=dis[now]+1) continue;
            dis[u]=dis[now]+1;
            if(!vis[u]){
                vis[u]=1;
                qu.push(u);
            }
        }
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        for(int i=0;i<17;i++){
            a[i+n]=(1<<i);
        }
        n+=17;

        SPFA();

        ll ans=0;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            ans+=(ll)dis[x^y]*i;
            ///printf("%I64d\n",ans);
            ans%=mod;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

D: Toposort

题意:给出n个点m条边的有向无环图. 要求删掉恰好k条边使得字典序最小的拓扑序列尽可能小.

思路:字典序最小的拓扑排序,首先我们只能删除入度<=K的节点,满足的节点又需要选择字典序最小的点,需要处理这两个操作,第一个操作可以用数组存在每个节点的入度然后放入线段树,线段树每个点存最小的点,第二个操作直接在线段树上找字典序最小的<=k的点。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<cmath>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
#define maxn 0x3f3f3f3f
#define MAX 1000100
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
#define INF (1ll<<60)-1
#define mod 1000000007
using namespace std;
int T,n,m,k,tot;
struct edge{
    int next,v;
}e[200100];
int head[100100],d[100100],tr[400100];
void init(){
    mst(head,-1);
    mst(d,0);
    tot=0;
}
void add(int u,int v){
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot++;
}
void build(int root,int l,int r){
    if(l==r){
        tr[root]=d[l];
        return ;
    }
    int mid=(l+r)/2;
    build(root*2,l,mid);
    build(root*2+1,mid+1,r);
    tr[root]=min(tr[root*2],tr[root*2+1]);
}
int query(int root,int l,int r,int K){
    if(l==r) return l;
    int mid=(l+r)/2;
    if(tr[root*2]<=K) return query(root*2,l,mid,K);
    else return query(root*2+1,mid+1,r,K);
}
void update(int root,int l,int r,int w){
    if(l==w && r==w){
        tr[root]=d[w];
        return ;
    }
    int mid=(l+r)/2;
    if(w<=mid) update(root*2,l,mid,w);
    else update(root*2+1,mid+1,r,w);
    tr[root]=min(tr[root*2],tr[root*2+1]);
}
int main(){
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            d[v]++;
        }
        build(1,1,n);
        ll ans=0;
        for(int i=1;i<=n;i++){
            int w=query(1,1,n,k);
            ///printf(" %%  %%\n");
            ans+=((ll)i*w);ans%=mod;
            k-=d[w];
            d[w]=MAX;
            update(1,1,n,w);
            ///printf("%%%%  \n");
            for(int j=head[w];j!=-1;j=e[j].next){
                d[e[j].v]--;
                ///printf("&&&^%^&%\n");
                update(1,1,n,e[j].v);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值