acwing 提高课图论

1315 问题 AS: 网格VI

时间限制: 1.000 Sec  内存限制: 128 MB
提交 状态

题目描述

某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标为B(n, m),其中n >= m。现在从A(0, 0)点出发,只能沿着街道向正右方或者正上方行走,且不能经过图示中直线左上方的点,即任何途径的点(x, y)都要满足x >= y,请问在这些前提下,到达B(n, m)有多少种走法。

输入

仅有一行,包含两个整数n和m,表示城市街区的规模。

输出

仅有一个整数和一个换行/回车符,表示不同的方案总数。

样例输入 Copy

6 6

样例输出 Copy

132

提示

100%的数据中,1 <= m <= n <= 5 000

#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int a[N],b[N];
int p[N],cnt;
bool st[N];
void init(int n)
{
    for(int i=2;i<=n;i++){
        if(!st[i])p[cnt++]=i;
        for(int j=0;p[j]*i<=n;j++){
            st[p[j]*i]=true;
            if(i%p[j]==0)break;
        }
    }
}
int get(int n,int p)
{
    int s=0;
    while(n)s+=n/p,n/=p;
    return s;
}
void mul(int r[],int& len,int x)
{
    int t=0;
    for(int i=0;i<len;i++){
        t+=r[i]*x;
        r[i]=t%10;
        t/=10;
    }
    while(t){
        r[len++]=t%10;
        t/=10;
    }
}
void sub(int a[],int al,int b[],int bl)
{
    for(int i=0,t=0;i<al;i++){
        a[i]-=t+b[i];
        if(a[i]<0)a[i]+=10,t=1;
        else t=0;
    }
}
int C(int x,int y,int r[N])
{
    int len=1;
    r[0]=1;
    for(int i=0;i<cnt;i++){
        int s=get(x,p[i])-get(y,p[i])-get(x-y,p[i]);
        while(s--)mul(r,len,p[i]);
    }
    return len;
}
int main()
{
    init(N-1);
    int n,m;
    cin>>n>>m;
    int al=C(n+m,m,a);
    int bl=C(n+m,n+1,b);
    sub(a,al,b,bl);
    int k=al-1;
    while(!a[k]&&k>0)k--;
    while(k>=0)cout<<a[k--];
    return 0;
}

1316  问题 AT: 有趣的数列

时间限制: 1.000 Sec  内存限制: 128 MB
提交 状态

题目描述

我们称一个长度为2n的数列是有趣的,当且仅当该数列满足以下三个条件:
    (1)它是从1到2n共2n个整数的一个排列{ai};
    (2)所有的奇数项满足a1<a3<…<a2n-1,所有的偶数项满足a2<a4<…<a2n;
    (3)任意相邻的两项a2i-1与a2i(1≤i≤n)满足奇数项小于偶数项,即:a2i-1<a2i。
    现在的任务是:对于给定的n,请求出有多少个不同的长度为2n的有趣的数列。因为最后的答案可能很大,所以只要求输出答案 mod P的值。

输入

只包含用空格隔开的两个整数n和P。输入数据保证,50%的数据满足n≤1000,100%的数据满足n≤1000000且P≤1000000000。

输出

仅含一个整数,表示不同的长度为2n的有趣的数列个数mod P的值。

样例输入 Copy

3 10

样例输出 Copy

5

提示

对应的5个有趣的数列分别为(1,2,3,4,5,6),(1,2,3,5,4,6),(1,3,2,4,5,6),(1,3,2,5,4,6),(1,4,2,5,3,6)。

#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef unsigned long long LL;
typedef pair<int,int>PII;
const int N=2000010;
int p[N],cnt;
bool st[N];
int n,m;
void init(int n)
{
    for(int i=2;i<=n;i++){
        if(!st[i])p[cnt++]=i;
        for(int j=0;p[j]*i<=n;j++){
            st[p[j]*i]=true;
            if(i%p[j]==0)break;
        }
    }
}
int qmi(int a,int k)
{
    int res=1;
    while(k)
    {
        if(k&1)res=(LL)res*a%m;
        a=(LL)a*a%m;
        k>>=1;
    }
    return res;
}
int get(int n,int p)
{
    int s=0;
    while(n){
        s+=n/p;
        n/=p;
    }
    return s;
}
int c(int a,int b)
{
    int res=1;
    for(int i=0;i<cnt;i++){
        int pr=p[i];
        int s=get(a,pr)-get(b,pr)-get(a-b,pr);
        res=(LL)res*qmi(pr,s)%m;
    }
    return res;
}

void solve()
{
    cin>>n>>m;
    init(n*2);
    //cout<<cnt;
    //return 0;
    cout<<(c(n*2,n)-c(n*2,n-1)+m)%m;

}
int main()
{

    int t=1;
    //cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

1172 祖孙询问

给定一棵包含 n 个节点的有根无向树,节点编号互不相同,但不一定是 1∼n。

有 m 个询问,每个询问给出了一对节点的编号 x 和 y,询问 x 与 y 的祖孙关系。

输入格式

输入第一行包括一个整数 表示节点个数;

接下来 n 行每行一对整数 a 和 b,表示 a 和 b 之间有一条无向边。如果 b 是 −1,那么 a 就是树的根;

第 n+2 行是一个整数 m 表示询问个数;

接下来 m 行,每行两个不同的正整数 x 和 y,表示一个询问。

输出格式

对于每一个询问,若 x 是 y 的祖先则输出 1,若 y 是 x 的祖先则输出 2,否则输出 0。

数据范围

1≤n,m≤4×104
1≤每个节点的编号≤4×104

输入样例:

10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19

输出样例:

1
0
0
0
2

#include <bits/stdc++.h>
#define x first
#define y second
#define endl "\n"
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 100010, mod = 1e6 + 7;
int f[N][16];
int d[N];
int e[N], ne[N], h[N], idx;
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void bfs(int u)
{
    memset(d, 0x3f, sizeof d);
    queue<int> q;
    q.push(u);
    d[u] = 1;
    d[0] = 0;
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (d[j] > d[t] + 1)
            {
                d[j] = d[t] + 1;
                q.push(j);
                f[j][0] = t;
                for (int k = 1; k <= 15; k++)
                {
                    f[j][k] = f[f[j][k - 1]][k - 1];
                }
            }
        }
    }
}
int lca(int a, int b)
{
    if (d[a] < d[b])
        swap(a, b);
    for (int k = 15; k >= 0; k--)
    {
        if (d[f[a][k]] >= d[b])
            a = f[a][k];
    }
    if (a == b)
        return a;
    for (int k = 15; k >= 0; k--)
    {
        if (f[a][k] != f[b][k])
        {
            a = f[a][k];
            b = f[b][k];
        }
    }
    return f[a][0];
}
void solve()
{
    int n;
    int root;
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        if (b == -1)
            root = a;
        else
            add(a, b), add(b, a);
    }
    bfs(root);
    int m;
    cin >> m;
    while (m--)
    {
        int a, b;
        cin >> a >> b;
        int p = lca(a, b);
        if (p == a)
            puts("1");
        else if (p == b)
            puts("2");
        else
            puts("0");
    }
}

int main()
{

    int t = 1;
    // scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

1171 距离

给出 n 个点的一棵树,多次询问两点之间的最短距离。

注意:

  • 边是无向的。
  • 所有节点的编号是 1,2,…,n

输入格式

第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数;

下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间存在一条边长度为 k;

再接下来 m 行,每行两个整数 x,y,表示询问点 x 到点 y 的最短距离。

树中结点编号从 1 到 n。

输出格式

共 m 行,对于每次询问,输出一行询问结果。

数据范围

2≤n≤104
1≤m≤2×104
0<k≤100
1≤x,y≤n

输入样例1:

2 2 
1 2 100 
1 2 
2 1

输出样例1:

100
100

输入样例2:

3 2
1 2 10
3 1 15
1 2
3 2

输出样例2:

10
25

#include<bits/stdc++.h>
#define x first
#define t second
using namespace std;
typedef pair<int,int>PII;
const int N=40010;
int n,m;
int h[N],ne[N],e[N],w[N],idx;
int d[N];
int p[N],res[N],st[N];
vector<PII>g[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
void dfs(int u,int fa)
{
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(j==fa)continue;
        d[j]=d[u]+w[i];
        dfs(j,u);
    }
}
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
void tar(int u)
{
    st[u]=1;
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            tar(j);
            p[j]=u;
        }
    }
    for(auto [x,y]:g[u]){
        if(st[x]==2){
            int h=find(x);
            res[y]=d[u]+d[x]-2*d[h];
        }
    }
    st[u]=2;
}
int main()
{
    cin>>n>>m;
    memset(h,-1,sizeof h);
    for(int i=1;i<n;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    for(int i=0;i<m;i++){
        int a,b;
        cin>>a>>b;
        if(a!=b){
            g[a].push_back({b,i});
            g[b].push_back({a,i});
        }
    }
    for(int i=1;i<=n;i++)p[i]=i;
    dfs(1,-1);
    tar(1);
    for(int i=0;i<m;i++)cout<<res[i]<<endl;
    return 0;
}

1185 单词游戏

有 N 个盘子,每个盘子上写着一个仅由小写字母组成的英文单词。

你需要给这些盘子安排一个合适的顺序,使得相邻两个盘子中,前一个盘子上单词的末字母等于后一个盘子上单词的首字母。

请你编写一个程序,判断是否能达到这一要求。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含整数 N,表示盘子数量。

接下来 N 行,每行包含一个小写字母字符串,表示一个盘子上的单词。

一个单词可能出现多次。

输出格式

如果存在合法解,则输出”Ordering is possible.”,否则输出”The door cannot be opened.”。

数据范围

1≤N≤105,
单词长度均不超过1000

输入样例:

3
2
acm
ibm
3
acm
malform
mouse
2
ok
ok

输出样例:

The door cannot be opened.
Ordering is possible.
The door cannot be opened.

 

#include<bits/stdc++.h>
using namespace std;
int din[30],dout[30];
int p[30];
bool st[30];
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];
}
int main()
{
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=0;i<26;i++)p[i]=i;
        memset(din,0,sizeof din);
        memset(dout,0,sizeof dout);
        memset(st,0,sizeof st);
        for(int i=0;i<n;i++){
            string s;
            cin>>s;
            int a=s[0]-'a',b=s.back()-'a';
            st[a]=st[b]=true;
            din[b]++,dout[a]++;
            a=find(a),b=find(b);
            p[a]=b;
        }
        int c1=0,c2=0;
        bool f=true;
        for(int i=0;i<26;i++){
            if(din[i]!=dout[i]){
                if(din[i]+1==dout[i])c1++;
                else if(din[i]==dout[i]+1)c2++;
                else{
                    f=false;
                    break;
                }
            }
        }
        if(st&&!(!c1&&!c2||c1==1&&c2==1))f=false;
        int s=-1;
        for(int i=0;i<26;i++){
            if(st[i]){
                if(s==-1)s=find(i);
                else if(s!=find(i)){
                    f=false;
                    break;
                }
            }
        }
        if(f)puts("Ordering is possible.");
        else puts("The door cannot be opened.");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值