BestCoder Round #74

LCP Array Accepts: 131
Submissions: 1352
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
Problem Description

Peter has a string s=s1s2…sns=s_{1}s_{2}…s_{n}s=s​1​​s​2​​…s​n​​, let suffi=sisi+1…sn\text{suff}i =s{i}s_{i+1}…s_{n}suff​i​​=s​i​​s​i+1​​…s​n​​ be the suffix start with iii-th character of sss. Peter knows the lcp (longest common prefix) of each two adjacent suffixes which denotes as ai=lcp(suffi,suffi+1)(1≤i

#include<iostream>
#include<stdio.h>
#include<vector>
#include<algorithm>
#include<vector>
#include<string.h>

int T;
int n;
int a[100005];
long long an=1;
int MOD=1000000007;
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int flag=1;
        an=26;
        for(int i=1;i<n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]!=a[i-1]-1&&a[i-1]>0)
            {
                flag=0;
            }
        }
        if(flag==0||a[n-1]>1)
        {            
            printf("0\n");
            continue;
        } 

            for(int i=1;i<n;i++)
            if(a[i]==0)
            an=(an*25)%MOD;

        printf("%I64d\n",an);
    }
}

Shortest Path Accepts: 40
Submissions: 610
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
Problem Description

There is a path graph G=(V,E)G=(V,E)G=(V,E) with nnn vertices. Vertices are numbered from 111 to nnn and there is an edge with unit length between iii and i+1i + 1i+1 (1≤i

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int di[8][8];
int n,m;
int a[8];
int aa,bb;
int an;
long long ans;

int abs(int x)
{
    if(x<0) return -x;
    return x;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=6;i++)
        scanf("%d",&a[i]);
        for(int i=1;i<=6;i++)
        for(int j=1;j<=6;j++)
        {
            di[i][j]=100005;            
        }
        for(int i=1;i<=6;i++)
        {
            di[i][i]=0;
            for(int j=1;j<=6;j++)
            {
                if((i&1)&&j==i+1)
                {

                    di[j][i]=di[i][j]=min(abs(a[j]-a[i]),1);
                }
                else di[i][j]=di[j][i]=min(abs(a[j]-a[i]),di[i][j]);
            }

        }
        //cout<<"     "<<di[1][2]<<endl;
        for(int k=1;k<=6;k++)
        for(int i=1;i<=6;i++)
        for(int j=1;j<=6;j++)
        if(i!=j&&j!=k&&k!=i)
        di[i][j]=min(di[i][j],di[i][k]+di[k][j]);
        ans=0;
        for(int ii=1;ii<=m;ii++)
        {
            scanf("%d%d",&aa,&bb);
            an=abs(aa-bb);
            for(int i=1;i<=6;i++)
            for(int j=1;j<=6;j++)
            {
                int QQ=di[i][j]+abs(aa-a[i])+abs(bb-a[j]);
                if(an>QQ) an=QQ;    
            }
            //cout<<an<<endl;
            ans=(ans+ii*an)%1000000007;
        }
        cout<<ans%1000000007<<endl;
    }
}

Transform Accepts: 7
Submissions: 49
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述

给出nnn个整数, 对于一个整数xxx, 你可以做如下的操作若干次:

  • 令xxx的二进制表示为b31b30…b0‾\overline{b_{31}b_{30}…b_0}​b​31​​b​30​​…b​0​​​​​, 你可以翻转其中一个位.
  • 令yyy是给出的其中一个整数, 你可以把xxx变为x⊕yx \oplus yx⊕y, 其中⊕\oplus⊕表示位运算里面的异或操作.

现在有若干整数对(S,T)(S, T)(S,T), 对于每对整数你需要找出从SSS变成TTT的最小操作次数.

输入描述

输入包含多组数据. 第一行有一个整数TTT (T≤20)(T \le 20)(T≤20), 表示测试数据组数. 对于每组数据:

第一行包含两个整数nnn和mmm (1≤n≤15,1≤m≤105)(1 \le n \le 15, 1 \le m \le 10^5)(1≤n≤15,1≤m≤10​5​​), 表示给出整数的数目和询问的数目. 接下来一行包含nnn个用空格分隔的整数a1,a2,…,ana_1, a_2, …, a_na​1​​,a​2​​,…,a​n​​ (1≤ai≤105)(1 \le a_i \le 10^5)(1≤a​i​​≤10​5​​).

接下来mmm行, 每行包含两个整数sis_is​i​​和tit_it​i​​ (1≤si,ti≤105)(1 \le s_i, t_i \le 10^5)(1≤s​i​​,t​i​​≤10​5​​), 代表一组询问.

输出描述

对于每组数据, 输出一个整数S=(∑i=1mi⋅zi) mod (109+7)S=(\displaystyle\sum_{i=1}^{m} i \cdot z_i) \text{ mod } (10^9 + 7)S=(​i=1​∑​m​​i⋅z​i​​) mod (10​9​​+7), 其中ziz_iz​i​​是第iii次询问的答案.

输入样例

1
3 3
1 2 3
3 4
1 2
3 9

输出样例

10

Hint

3→43 \to 43→4 (2次操作): 3→7→43 \to 7 \to 43→7→4

1→21 \to 21→2 (1次操作): 1⊕3=21 \oplus 3 = 21⊕3=2

3→93 \to 93→9 (2次操作): 3→1→93 \to 1 \to 93→1→9

solution: 1.DP 2.bfs
add edge from 0 :1.(1<

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
long long  a[18+15+1];
long long ans;
int MAXN=131072+1;
long long MOD=1e9+7;
int dp[131073*2];
int n,m;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)   scanf("%lld",&a[i]);
        for(int i=0;i<18;i++)
        {
            a[++n]=(1<<i);
        }   
        for(int i=1;i<MAXN;i++)    dp[i]=99999;
        for(int i=1;i<=n;i++)
        for(int j=0;j<MAXN;j++)
        {
            dp[a[i]^j]=min(   dp[a[i]^j],dp[j]+1    );
        }
        for(int  i=1;i<=m;i++)
        {
            int aa,bb;
            scanf("%d%d",&aa,&bb);
            ans=(ans+(long long )dp[aa^bb]*(long long )i)%MOD;
        }
        cout<<ans<<endl;
    }
}

Toposort Accepts: 7
Submissions: 25
Time Limit: 10000/5000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述

给出nnn个点mmm条边的有向无环图. 要求删掉恰好kkk条边使得字典序最小的拓扑序列尽可能小.

输入描述

输入包含多组数据. 第一行有一个整数TTT, 表示测试数据组数. 对于每组数据:

第一行包含3个整数nnn, mmm和kkk (1≤n≤100000,0≤k≤m≤200000)(1 \le n \le 100000, 0 \le k \le m \le 200000)(1≤n≤100000,0≤k≤m≤200000), 表示图中结点数目, 图中边的数目以及要删的边数.

接下来mmm行, 每行包含两个整数uiu_iu​i​​ and viv_iv​i​​, 表示存在一条uiu_iu​i​​到viv_iv​i​​的有向边 (1≤ui,vi≤n)(1 \le u_i, v_i \le n)(1≤u​i​​,v​i​​≤n).

输入保证给定的图是一个DAG. 输入数据中nnn的和不超过10610^610​6​​. 输入数据中mmm的和不超过2⋅1062 \cdot 10^62⋅10​6​​.

输出描述

对于每组数据, 输出一个整数S=(∑i=1ni⋅pi) mod (109+7)S = (\displaystyle\sum_{i=1}^{n}{i\cdot p_i}) \text{ mod } (10^9 + 7)S=(​i=1​∑​n​​i⋅p​i​​) mod (10​9​​+7), 其中p1,p2,…,pnp_{1}, p_{2}, …, p_{n}p​1​​,p​2​​,…,p​n​​是字典序最小的那个拓扑序列.

输入样例

3
4 2 0
1 2
1 3
4 5 1
2 1
3 1
4 1
2 3
2 4
4 4 2
1 2
2 3
3 4
1 4

输出样例

30
27
30
solution 1:Toposort

参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于kkk的所有点, 每次找出编号最小的, 并相应的减少kkk即可.

这个数据结构可以用线段树, 建立一个线段树每个节点[l,r][l,r][l,r]维护编号从lll到rrr的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的xxx满足入度小于等于kkk.

复杂度O((n+m)logn)O((n+m)\log n)O((n+m)logn)
solution 2:
maintain a pri. queue to faster locate the curent best answer;

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#define mod 1000000007
using namespace std;
int T;
int n,m,k;
int aa,bb;
int r[100005];
int vis[100005];
vector<int> lin[100005];
int tot;
long long ans;
priority_queue<int> q;
void TP()
{
    tot=0;
    while(tot<n)
    {
        int now=-q.top();
        q.pop();
        if(r[now]>k||vis[now]==1) continue;
        vis[now]=1;
        k-=r[now];
        r[now]=0;  
        tot++;
        ans+=(long long)tot*(long long)now;
        ans%=mod;   
        for(int i=0;i<lin[now].size();i++)
        {
            int nex=lin[now][i];
            if(r[nex])
            {
                r[nex]--;
                q.push(-nex);
            }
        }
    }

}




int main()
{
    scanf("%d",&T); 
    while(T--)
    {
        ans=0;
        while(!q.empty()) q.pop();
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=n;i++)
        {
            lin[i].clear();
            vis[i]=r[i]=0;                      
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&aa,&bb);
            lin[aa].push_back(bb);
            r[bb]++;
        }
        for(int i=1;i<=n;i++) q.push(-i);
        TP();
        cout<<ans<<endl;
    }
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值