2017-10-7 清北刷题冲刺班p.m

测试


A 同花顺


文件名 输入文件 输出文件 时间限制 空间限制
card.cpp/c/pas card.in card.out 1s 512MB
题目描述
所谓同花顺,就是指一些扑克牌,它们花色相同,并且数字连续。
现在我手里有 n 张扑克牌,但它们可能并不能凑成同花顺。我现在想知道,最
少更换其中的多少张牌,我能让这 n 张牌凑成一个同花顺?
输入格式
第一行一个整数 n,表示扑克牌的张数。
接下来 n 行,每行两个整数 a i 和 b i 。其中 a i 表示第 i 张牌的花色,b i 表示第
i 张牌的数字。
(注意: 这里的牌上的数字不像真实的扑克牌一样是 1 到 13, 具体见数据范围)
输出格式
一行一个整数,表示最少更换多少张牌可以达到目标。
样例输入 1
5
1 1
1 2
1 3
1 4
1 5
样例输出 1
0
样例输入 2
5
1 9
1 10
2 11
2 12
2 13
样例输出 2
2
数据范围
对于 30% 的数据,n ≤ 10。
对于 60% 的数据,n ≤ 10 5 ,1 ≤ a i ≤ 10 5 ,1 ≤ b i ≤ n。
对于 100% 的数据,n ≤ 10 5 ,1 ≤ a i ,b i ≤ 10 9 。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,ans,cnt;
struct node{
    int col,num;
}a[100010],q[100010];
int cmp(node x,node y){
    if(x.col==y.col)return x.num<y.num;
    return x.col<y.col;
}
int main(){
    freopen("card.in","r",stdin);freopen("card.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i].col,&a[i].num);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        if(a[i].col==a[i-1].col&&a[i].num==a[i-1].num)continue;
        q[++cnt]=a[i];
    }
    for(int i=1;i<=cnt;i++){
        int tmp=0;
        for(int j=i;j>=1;j--){
            if(q[j].col==q[i].col&&q[i].num-q[j].num+1<=n)tmp++;
            else break;
        }
        ans=max(ans,tmp);
    }
    printf("%d",n-ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
100分


B 做实验


文件名 输入文件 输出文件 时间限制 空间限制
test.pas/c/cpp test.in test.out 1s 128MB
题目描述
有一天,你实验室的老板给你布置的这样一个实验。
首先他拿出了两个长度为 n 的数列 a 和 b,其中每个 a i 以二进制表示一个集
合。例如数字 5 = (101) 2 表示集合 {1,3}。第 i 次实验会准备一个小盒子,里面装
着集合 a i 所有非空子集的纸条。老板要求你从中摸出一张纸条,如果满足你摸出的
纸条是 a i 的子集而不是 a i−b i ,a i−b i +1 ,...,a i−1 任意一个的子集,那么你就要 ***;
反之,你就逃过一劫。
令你和老板都没有想到的是,你竟然每次都逃过一劫。在庆幸之余,为了知道
这件事发生的概率,你想要算出每次实验有多少纸条能使你 ***
输入格式
第一行一个数字 n。
接下来 n 行,每行两个整数,分别表示 a i 和 b i 。
输出格式
n 行,每行一个数字,表示第 i 次实验能使你 *** 的纸条数。
样例输入 1
3
7 0
15 1
3 1
样例输出 1
7
8
0
数据范围
对于 30% 的数据,n,a i ,b i ≤ 100
对于 70% 的数据,n,a i ,b i ≤ 60000
对于 100% 的数据,n,a i ,b i ≤ 10 5
保证所有的 a i 不重复,b i < i

#include<iostream>
#include<cstdio>
#define maxn 100010
using namespace std;
int n,a[maxn],b[maxn],l[maxn],sum[maxn][32],s[maxn][32],have[maxn][32];
int bin[1000],len;
void make(int num,int x){
    len=0;
    while(x){
        bin[++len]=x&1;
        x>>=1;
    }
    for(int i=len;i>=1;i--){
        if(bin[i]){
            sum[num][i]++;
            have[num][++have[num][0]]=i;
        }
    }
    l[num]=len;
}
int Pow(int x,int y){
    int res=1;
    while(y){
        if(y&1)res=res*x;
        x=x*x;
        y>>=1;
    }
    return res;
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("test.in","r",stdin);freopen("test.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]);
    for(int i=1;i<=n;i++)//枚举每次实验 
        make(i,a[i]);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=31;j++)
            s[i][j]=s[i-1][j]+sum[i][j];
    }
    for(int i=1;i<=n;i++){
        long long ans=0;
        int k=0;
        int now=Pow(2,have[i][0]-1);
        for(int j=1;j<=have[i][0];j++){//循环集合中的每个数 
            if(s[i-1][have[i][j]]-s[i-b[i]-1][have[i][j]]==0){
                ans+=Pow(2,have[i][0]-k-1);k++;
            }
        }
        printf("%I64d
",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}
0分 暴力
/*
    枚举一个数的二进制子集的方法:
    for(int i=s;i;i=(i-1)&s)
    记录一下子集s最晚出现的位置 
*/
#include<iostream>
#include<cstdio>
using namespace std;
int n,a,b,ans,f[100010];
int main(){
    freopen("test.in","r",stdin);freopen("test.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&a,&b);
        ans=0;
        for(int j=a;j;j=(j-1)&a){
            if(f[j]<i-b)ans++;
            f[j]=i;
        }
        printf("%d
",ans);
    }
    fclose(stdin);fclose(stdout);
    return 0;
}
100分 枚举子集+记录最晚出现的位置

C 拯救世界(Tarjan)


文件名 输入文件 输出文件 时间限制 空间限制
save.cpp/c/pas save.in save.out 1s 512MB
题目描述
C 城所有的道路都是单向的。不同道路之间有路口,每个路口都有一个大楼。
有一天, 城市里的所有大楼因为不明原因, 突然着火了。 作为超人的你要去拯救
这些大楼。初始的时候你在 S 号楼,最后你必须到达某个有补给站的大楼,你可以
沿着单向道路行驶。你可以经过某条道路或者某个大楼若干次,经过一个大楼你就
可以消灭一个大楼的大火。每个大楼都有一个重要程度,最后这个任务的评价分数
就是你经过的所有大楼的重要度之和(若重复经过某个大楼多次,则不重复算分) 。
你是一个聪明的超人,你想知道,通过合理的规划路线,你这次任务能得到的
最高得分是多少。
注意,该城市的道路可能有重边或自环。
输入格式
第一行包括两个整数 n,m,n 表示路口的个数(即大楼的个数) ,m 表示道路
的条数。
接下来 m 行,每行两个整数 x,y,表示 x 到 y 之间有一条单向道路。
接下来 n 行,每行一个整数,按顺序表示每个大楼的重要度。
接下来一行包含两个整数 S 和 P,S 是出发的路口(大楼)的编号,P 是有补
给站的大楼的数量。
接下来一行 P 个整数,表示有补给站的大楼的编号。
输出格式
输出一行一个整数,表示你得分的最大值。
样例输入 1
6 7
1 2
2 3
3 5
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
样例输出 1
47
数据范围
对于 1、2、3 测试点,N,M ≤ 300
对于 4、5、6、7、8、9、10 测试点,N,M ≤ 3000
对于 11、12、13、14、15 测试点,N,M ≤ 500000。每个大楼的重要度均为非
负数且不超过 4000。
输入数据保证你可以从起点沿着单向道路到达其中的至少一个有补给站的大
楼。
注意,输入数据中存在树和链的特殊情况

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 500000
using namespace std;
int n,m,num,head[maxn],head1[maxn],w[maxn],t[maxn],s,num1,w1[maxn];
int ans=0;
struct node{
    int to,pre;
}e[maxn],e1[maxn];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
int dfn[maxn],low[maxn],st[maxn],belong[maxn];
bool in_st[maxn];
int s_num,cnt,group_num;
void group(int u){
    cnt++;st[++s_num]=u;dfn[u]=low[u]=cnt;in_st[u]=1;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(!dfn[v]){
            group(v);
            if(low[v]<low[u])low[u]=low[v];
        }
        else if(dfn[v]<low[u])
            if(in_st[v])
            low[u]=dfn[v];
    }
    if(dfn[u]==low[u]){
        group_num++;
        while(st[s_num]!=u){
            in_st[st[s_num]]=0;
            belong[st[s_num]]=group_num;
            s_num--;
        }
        in_st[u]=0;s_num--;
        belong[u]=group_num;
    }
}
void Insert2(int from,int to){
    e1[++num1].to=to;
    e1[num1].pre=head1[from];
    head1[from]=num1;
}
bool ok[maxn];
void dfs(int now,int sum){
    if(ok[now])
        ans=max(ans,sum);
    for(int i=head1[now];i;i=e1[i].pre){
        int to=e1[i].to;
        dfs(to,sum+w1[to]);
    }
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("save.in","r",stdin);freopen("save.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);
    }
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    int t_num;
    scanf("%d%d",&s,&t_num);
    for(int i=1;i<=t_num;i++)scanf("%d",&t[i]);
    for(int i=1;i<=n;i++){
        if(!dfn[i])group(i);
    }
    for(int now=1;now<=n;now++){
        w1[belong[now]]+=w[now];
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(belong[now]!=belong[to])Insert2(belong[now],belong[to]);
        }
    }
    for(int i=1;i<=t_num;i++){
        ok[belong[t[i]]]=1;
    }
    ans=w1[belong[s]];
    dfs(belong[s],w1[belong[s]]);
    printf("%d",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
72分 Tarjan缩点+dfs最短路
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 500010
using namespace std;
int n,m,num,head[maxn],head1[maxn],w[maxn],t[maxn],s,num1,w1[maxn];
int ans=0;
struct node{
    int to,pre;
}e[maxn],e1[maxn];
void Insert(int from,int to){
    e[++num].to=to;
    e[num].pre=head[from];
    head[from]=num;
}
int dfn[maxn],low[maxn],st[maxn],belong[maxn];
bool in_st[maxn];
int s_num,cnt,group_num;
void group(int u){
    cnt++;st[++s_num]=u;dfn[u]=low[u]=cnt;in_st[u]=1;
    for(int i=head[u];i;i=e[i].pre){
        int v=e[i].to;
        if(!dfn[v]){
            group(v);
            if(low[v]<low[u])low[u]=low[v];
        }
        else if(dfn[v]<low[u])
            if(in_st[v])
            low[u]=dfn[v];
    }
    if(dfn[u]==low[u]){
        group_num++;
        while(st[s_num]!=u){
            in_st[st[s_num]]=0;
            belong[st[s_num]]=group_num;
            s_num--;
        }
        in_st[u]=0;s_num--;
        belong[u]=group_num;
    }
}
void Insert2(int from,int to){
    e1[++num1].to=to;
    e1[num1].pre=head1[from];
    head1[from]=num1;
}
queue<int>q;
int vis[maxn],dis[maxn];
void spfa(){
    dis[belong[s]]=w1[belong[s]];
    vis[belong[s]]=1;
    q.push(belong[s]);
    while(!q.empty()){
        int now=q.front();q.pop();vis[now]=0;
        for(int i=head1[now];i;i=e1[i].pre){
            int to=e1[i].to;
            if(dis[to]<dis[now]+w1[to]){
                dis[to]=dis[now]+w1[to];
                if(!vis[to]){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
    }
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("save.in","r",stdin);freopen("save.out","w",stdout);
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        Insert(x,y);
    }
    for(int i=1;i<=n;i++)scanf("%d",&w[i]);
    int t_num;
    scanf("%d%d",&s,&t_num);
    for(int i=1;i<=t_num;i++)scanf("%d",&t[i]);
    for(int i=1;i<=n;i++){
        if(!dfn[i])group(i);
    }
    for(int now=1;now<=n;now++){
        w1[belong[now]]+=w[now];
        for(int i=head[now];i;i=e[i].pre){
            int to=e[i].to;
            if(belong[now]!=belong[to])Insert2(belong[now],belong[to]);
        }
    }
    ans=w1[belong[s]];
    spfa(); 
    for(int i=1;i<=t_num;i++){
        ans=max(ans,dis[belong[t[i]]]);
    }
    printf("%d",ans);
    fclose(stdin);fclose(stdout);
    return 0;
}
90分(满分) Tarjan缩点+spfa
void topsort()
{
    st[top=1]=id[S]; dp[id[S]]=sum[id[S]];
    int now;
    while(top)
    {
        now=st[top--];
        for(int i=front2[now];i;i=nxt2[i]) 
        {
            dp[to2[i]]=max(dp[to2[i]],dp[now]+sum[to2[i]]);
            in[to2[i]]--;
            if(!in[to2[i]]) st[++top]=to2[i];
        }
    }
}
拓扑排序
原文地址:https://www.cnblogs.com/thmyl/p/7635087.html