The 2021 Sichuan Provincial Collegiate Programming Contest

E.Don’t Really Like How The Story Ends

E.Don’t Really Like How The Story Ends

题意:给你一个无向图,然你进行dfs,同时你可以进行加边操作,使得最后dfs序是1~n。

解题思路:dfs,我们在进行dfs时遍历该节点的子节点(事先对该节点的所有子节点进行排序),如果有目标节点则先将目标加一再对原来的目标进行dfs,如果没有目标点而且大于目标点,我们就需要加边,其实加边都加在根节点1上,此时还要将目标加一再对原来的目标进行dfs。那什么时候退出递归呢,我们在一开始可以加入一个虚拟节点n+1,当我们遍历到这个虚拟节点就可以退出了。

总而言之:

1.碰到小于目标的continue

2.碰到等于目标的,更新目标继续递归

3.碰到大于目标的,加边继续递归

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
#define ll long long
//#define int long long
const int maxn = 1e5 + 20;
const int inf = 0x3f3f3f3f;
const ll INF = 1ll << 62;
const double eps = 1e-7;
const int mod = 1e6 + 3;
#define mem(a, b) memset(a, b, sizeof(a))
 
inline ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}
 
inline int rd()
{
    int x;
    scanf("%d", &x);
    return x;
}
 
int n, m, res, want;
vector<int>e[maxn];
 
void addEdge(int u, int v){
    e[u].push_back(v);
    e[v].push_back(u);
}
 
void dfs(int u){
    if(u == n + 1)return;
    for(auto v : e[u]){
        if(v < want)continue;
        while(v >= want){//更新目标,加边,利用循环可以不断添加(可以手动模拟理解)
            if(v == want) want++, dfs(want - 1);
            else want++, res++, dfs(want - 1);
        }
    }
}
 
int main(){
    int T = rd();
    while(T--){
        n = rd();
        m = rd();
        for(int i = 1; i <= n; i++)e[i].clear();
        for(int i = 0; i < m; i++){
            int u = rd();
            int v = rd();
            addEdge(u, v);
        }
        addEdge(1, n + 1);//增加虚拟节点
        for(int i = 1; i <= n; i++)sort(e[i].begin(), e[i].end());
        res = 0;
        want = 2;
        dfs(1);
        printf("%d\n", res);
    }
 
    return 0;
}

FDirection Setting 

F (codeforces.com)

题意:给出一个n个点m条边的无向图,给出a1,a2,.....an 对于每条无向边你可以让它成为有向边,记每个点的度为di 求(d1-a1+d2-a2+.....+dn-an)的最小值

思路:

 

 

代码:会建图了套下板子就行

#include<bits/stdc++.h>
using namespace std;
const int MAXN=6000,MAXM=101000;
const int INF=0x3f3f3f3f;
struct edge{
    int to,next,cap,flow,cost;
}e[MAXM];
int head[MAXN],cnt;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}

void init(int n){
    N=n;
    cnt=0;
    memset(head,-1,sizeof head);
}
void add(int u,int v,int cap,int cost){
    e[cnt].to=v;
    e[cnt].cap=cap;
    e[cnt].cost=cost;
    e[cnt].flow=0;
    e[cnt].next=head[u];
    head[u]=cnt++;
}

bool spfa(int s,int t){
    queue<int>q;
    for(int i=0;i<MAXN;i++){
        dis[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[s]=0;
    vis[s]=true;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].to;
            if(e[i].cap>e[i].flow&&dis[v]>dis[u]+e[i].cost){
                dis[v]=dis[u]+e[i].cost;
                pre[v]=i;
                if(!vis[v]){
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    int ed=t;
    if(pre[t]==-1)return false;
    else return true;
}
int minCostMaxflow(int s,int t,int &cost){
    int flow=0;
    cost=0;
    while(spfa(s,t)){
        int Min=INF;
        for(int i=pre[t];~i;i=pre[e[i^1].to]){
            if(Min>e[i].cap-e[i].flow)
                Min=e[i].cap-e[i].flow;
        }
        for(int i=pre[t];~i;i=pre[e[i^1].to]){
            e[i].flow+=Min;
            e[i^1].flow-=Min;
            cost+=e[i].cost*Min;
        }
        flow+=Min;
    }
    return flow;
}
int main(){
    int T=read();
    while(T--){
        int n=read(),m=read(),s=800,t=801;
        init(n);
        vector<int>a(n+1);
        for(int i=1+n;i<=m+n;i++){
            add(s,i,1,0);
            add(i,s,0,0);
        }
        for(int i=1;i<=n;i++){
            cin>>a[i];
            int u=i,v=t;
            add(u,v,a[i],0);
            add(v,u,0,0);
            add(u,v,INF,1);
            add(v,u,0,-1);
        }
        for(int i=1+n;i<=n+m;i++){
            int v1,v2,u=i;
            cin>>v1>>v2;
//            if(v1==v2)continue;
            add(u,v1,1,0);
            add(v1,u,0,0);
            add(u,v2,1,0);
            add(v2,u,0,0);
        }
        int cost;
        int maxflow=minCostMaxflow(s,t,cost);
        vector<int>ans;
        for(int u=n+1;u<=n+m;u++){
            int i=head[u];
            ans.push_back(!e[i].flow);
        }
        printf("%d\n",cost);
        for(auto x:ans)cout<<x;
        cout<<endl;
    }
}

 

L. Spicy Restaurant

L. Spicy Restaurant

题意:很多人想要去低于等于自己能够承受辣度的店面,店面由自己的辣度,店面之间存在道路,人在不同的店面,问每个人的最短距离是多少?因为店面和人的数量达到1e5,询问由5e5(⊙﹏⊙),所以不能暴力。

解题思路:多源bfs,对每一种辣度进行bfs求出某一个点到某一个辣度的最小距离,然后就可以进行O(n)的询问了

多源bfs

void bfs(int pos){
    queue<int>q;
    for(int i=1;i<=n;i++)vis[i]=0;
    for(int i=1;i<=n;i++){
        if(ld[i]==pos)q.push(i),dis[i][pos]=0,vis[i]=1;//记录每一个当前辣度的店铺点
    }
//多源bfs就是把多个点放进queue中,然后就和普通的bfs没区别了
    while(!q.empty()){
        int index=q.front();
        q.pop();
        for(int i=0;i<ve[index].size();i++){
            int net=ve[index][i];
            if(!vis[net]){
                vis[net]=1;
                dis[net][pos]=dis[index][pos]+1;
                q.push(net);
            }
        }
    }
 
}

完整代码

#include "bits/stdc++.h"
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
int n,m,q;
int ld[maxn],vis[maxn];
vector<int>ve[maxn];
int dis[maxn][110];
inline int rd(){
    int a;
    scanf("%d",&a);
    return a;
}
void bfs(int pos){
    queue<int>q;
    for(int i=1;i<=n;i++)vis[i]=0;
    for(int i=1;i<=n;i++){
        if(ld[i]==pos)q.push(i),dis[i][pos]=0,vis[i]=1;
    }
    while(!q.empty()){
        int index=q.front();
        q.pop();
        for(int i=0;i<ve[index].size();i++){
            int net=ve[index][i];
            if(!vis[net]){
                vis[net]=1;
                dis[net][pos]=dis[index][pos]+1;
                q.push(net);
            }
        }
    }

}
int main(){
    memset(dis,inf,sizeof dis);
    n=rd();m=rd();
    q=rd();
    for(int i=1;i<=n;i++){
        ld[i]=rd();
    }
    for(int i=1;i<=m;i++){
        int a=rd(),b=rd();
        ve[a].push_back(b);
        ve[b].push_back(a);
    }
    for(int i=1;i<=100;i++){
        bfs(i);
    }
//    for(int i=1;i<=n;i++){
//        for(int j=1;j<=5;j++){
//            cout<<"shop"<<i<<" to ld"<<j<<" =="<<dis[i][j]<<endl;
//        }
//    }
    for(int i=1;i<=q;i++){
        int a=rd(),b=rd();
        int ans=inf;
        for(int j=1;j<=b;j++)ans=min(ans,dis[a][j]);
        if(ans==inf)printf("-1\n");
        else printf("%d\n",ans);
    }
    return 0;
}

 Don’t Really Like How The Story Ends

Don’t Really Like How The Story Ends

题意:给了一群1到n的行星,部分行星之间存在路径,问你添加最少几条路径使得从1号点dfs结果可以是1到n

题解:存在三种情况

1.i和i+1号行星存在路径,那就直接dfs第i+1号行星

2.i和i+1号行星之间没有路径,那就添加一条路径,然后dfs第i+1号行星,然后回溯的时候再遍历和i号点相连的行星

3.回溯到1号行星都没有路径和第i号行星相连,就添加一条路径,然后dfs第i号行星

关键节点:

1.情况1和2

if(net==now){
            vis[now]=1;
            dfs(net,now);
        }else{
            cnt++;
            vis[now]=1;
            dfs(now,now);
            i--;
        }

2.情况3

while(now<=n){
//            cout<<now<<" "<<cnt<<endl;
            vis[now]=1;if(now!=1)cnt++;dfs(now,now);
        }

AC代码:

#include "bits/stdc++.h"
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
 
inline int rd(){
    int a;
    scanf("%d",&a);
    return a;
}
int t,n,m;
vector<int>ve[maxn];
int now,cnt,vis[maxn];
void dfs(int pos,int &now){
    now++;
    for(int i=0;i<ve[pos].size();i++){
//        cout<<ve[pos][i]<<" "<<now<<endl;
        int net=ve[pos][i];
        if(vis[net])continue;
        if(net==now){
            vis[now]=1;
            dfs(net,now);
        }else{
            cnt++;
            vis[now]=1;
            dfs(now,now);
            i--;
        }
    }
}
 
int main(){
    t=rd();
    while(t--){
        n=rd();m=rd();
        for(int i=1;i<=n;i++){
            ve[i].clear();
        }
        for(int i=1;i<=m;i++){
            int a=rd(),b=rd();
            if(a>b)ve[b].push_back(a);
            else ve[a].push_back(b);
        }
        for(int i=1;i<=n;i++){
            sort(ve[i].begin(),ve[i].end());
            vis[i]=0;
        }
        now=1;cnt=0;
        while(now<=n){
//            cout<<now<<" "<<cnt<<endl;
            vis[now]=1;if(now!=1)cnt++;dfs(now,now);
        }
        printf("%d\n",cnt);
    }
    return 0;
}

 Monster Hunter

Monster Hunter

怪猎狂喜题

题意:有n只怪物,给你m个攻击,伤害就是循环执行的那种(伤害值就是 1 to 3 ),然后问你最少要多少次攻击才能击杀所有怪物,嘿嘿

题解:本来以为是贪心,emm。。。。确实没错,是二分加贪心

二分能够击杀所有怪物的次数,然后就得到了每一种伤害能够使用的次数,然后用贪心去杀怪物,能杀完就减少次数继续二分,不能也二分,直到出结果、

也就是说用二分把题目变成一个填充值为1、2、3的贪心问题,然后重点来了

贪心策略:1.对于每一个血量大于等于3的奇数的怪物用3去打一次伤害,把这一类的奇数血量打成偶数,(方便后面用2和1去填充)

2.对于血量大于等于6的怪物用一组一组的3去打伤害,打到最后就只剩下1、2、4三种类型的血量或者3打完了(就直接1、2贪心不要接下来的步骤了)

3.如果我们还有3多,那就直接从多的血量打到少的血量,再从少的血量打到多的血量,(就是n到1再到n的意思)来回碾压一遍,因为最大是4所以必定能血量打完,然后因为有1、2的存在,所以必定3打的完,否则要么没1、2,要么就能下一个二分了

4.3打完了以后就用1、2伤害贪心,先用2用到没,再用1补充

AC代码

#include "bits/stdc++.h"
using namespace std;
#define int long long
#define ll long long
const int maxn=1e5+100;
inline int rd(){
    int a;
    scanf("%lld",&a);
    return a;
}
int t,n,m;
int a[maxn],num[maxn][10],hp[maxn];

int check(int h[],vector<int>ve){
    int hp[m+10];
    for(int i=0;i<m;i++)hp[i]=h[i];
    //情况1
    for(int i=0;i<m;i++){
        if(hp[i]>=3&& (hp[i]&1) &&ve[2]){
            hp[i]-=3;
            ve[2]--;
        }
    }
    //情况2
    for(int i=0;i<m;i++){
        if(hp[i]>=6&&ve[2]) {
            int num = min(hp[i] / 6, ve[2] / 2);
            hp[i] -= num *6;
            ve[2] -= num*2;
        }
    }
    //情况3
    sort(hp,hp+m);
    for(int i=m-1;i>=0;i--){
        if(ve[2]&&hp[i]){
            hp[i]=max(0ll,hp[i]-3);
            ve[2]--;
        }
    }
    for(int i=0;i<m;i++){
        if(ve[2]&&hp[i]){
            hp[i]=max(0ll,hp[i]-3);
            ve[2]--;
        }
    }
    //情况4
    for(int i=0;i<m;i++){
        if(ve[1]&&hp[i]){
            int num=min(hp[i]/2,ve[1]);
            hp[i]-=2*num;
            ve[1]-=num;
        }
    }
    for(int i=0;i<m;i++){
        if(ve[1]&&hp[i]){
            hp[i]=max(0ll,hp[i]-2);
            ve[1]--;
        }
    }
    for(int i=0;i<m;i++){
        if(ve[0]&&hp[i]){
            int num=min(hp[i],ve[0]);
            ve[0]-=num;
            hp[i]-=num;
        }
    }
    
    //看最大的怪物血量是不是0
    sort(hp,hp+m);
    if(hp[m-1]!=0)return 0;
    return 1;
}

signed main(){
    t=rd();
    while(t--){
        n=rd();
        for(int i=1;i<=n;i++){
            a[i]=rd();
            for(int j=0;j<3;j++)num[i][j]=num[i-1][j];
            num[i][a[i]-1]++;//前缀和记录第i个攻击时有几个1,几个2,几个3
        }
        m=rd();
        for(int i=0;i<m;i++){
            hp[i]=rd();
        }
        int l=0,r=1e16;//二分伤害,wa4就是r太小了
        while(l<r){
            int mid=(l+r)>>1;
            vector<int>ve;
            int t=mid/n;int w=mid%n;//t就是有几个组,w就是多出来的
            for(int i=0;i<3;i++){
                ve.push_back(num[w][i]+t*num[n][i]);
                //所以有几个i的伤害就是t组*每组里i的个数加上前w里有几个i
            }
            if(check(hp,ve)==1){
                r=mid;
            }else l=mid+1;
        }
        printf("%lld\n",l);
    }
    return 0;
}

 

posted @ 2021-07-03 14:44  塔塔开  阅读(312)  评论(0)    收藏  举报