BestCoder Round #74 (div.2)

26 篇文章 0 订阅
16 篇文章 0 订阅

未完待续~~
划分等价类,判断是否无解,26*25^(m-1)即可,倒着来~
​​

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
using namespace std;

#define N 100010
#define Mod 1000000007LL

int n;
int A[N];
long long Dp[N];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int main()
{
    int T=read();
    while(T--)
    {
        n=read();
        memset(Dp,0,sizeof(Dp));
        memset(A,0,sizeof(A));
        Dp[n]=26;
        for(int i=1;i<=n-1;i++)
            A[i]=read();
        for(int i=n-1;i>=1;i--)
        {
            if(A[i]==0)
                Dp[i]=Dp[i+1]*25%Mod;
            else if(A[i]==A[i+1]+1)
                Dp[i]=Dp[i+1];
            else
            {
                Dp[i]=0;
                break;
            }
        }
        printf("%I64d\n",Dp[1]);
    }
    return 0;
}

直接对新的点建图,然后flyod,最后算答案的是后6*6,枚举转折点更新答案即可!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
int a[10],dis[10][10],n,m;
const ll mod=1e9+7;
int main(){
    int t;cin>>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++){
                dis[i][j]=abs(a[i]-a[j]);
            }
        }
        for(int i=1;i<=5;i+=2){
            dis[i][i+1]=dis[i+1][i]=1;
        }
        for(int k=1;k<=6;k++){
            for(int i=1;i<=6;i++){
                for(int j=1;j<=6;j++){
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
                }
            }
        }
        int s,t;ll anss=0;
        for(ll k=1;k<=m;k++){
            scanf("%d%d",&s,&t);
            ll ans=abs(s-t);
            for(int i=1;i<=6;i++){
                for(int j=1;j<=6;j++){
                    ans=min(ans,(ll)(abs(s-a[i])+abs(t-a[j])+dis[i][j]));
                }
            }
            anss=(anss+k*ans)%mod;
        }
        printf("%lld\n",anss);
    }

}

利用异或性质进行bfs,O(1)查询!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int maxn=1e6+10;
int dis[maxn],vis[maxn];
int n,m,a[20],s,t;
void bfs(){
    queue<int>q;
    while(!q.empty())q.pop();
    q.push(0);dis[0]=0;vis[0]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=0;i<17;i++){
            if(vis[(1<<i)^now]) continue;
            dis[(1<<i)^now]=dis[now]+1;q.push((1<<i)^now);
            vis[(1<<i)^now]=1;
        }
        for(int i=1;i<=n;i++){
            if(vis[a[i]^now]) continue;
            dis[a[i]^now]=dis[now]+1;q.push(a[i]^now);
            vis[a[i]^now]=1;
        }
    }
}
int main(){
    //cout<<"== "<<(1<<15)<<endl;
    int tt;cin>>tt;
    while(tt--){
        ll anss=0;
        scanf("%d%d",&n,&m);memset(dis,0x7f7f7f7f,sizeof(dis));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
        bfs();
        for(ll i=1;i<=m;i++){
            scanf("%d%d",&s,&t);
            anss=(anss+i*dis[s^t])%mod;
        }
        printf("%lld\n",anss);
    }

}

题意:求删除恰好k条边后,能够使得字典序最小的拓扑序列~
题解:参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于kk的所有点, 每次找出编号最小的, 并相应的减少k即可.

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

这个结构也可用优先队列,每次把入度小于等于k的入队,弹出序号最小的标号,把它作为拓扑的下一个,代价就是把它的入度全部用k删掉,然后把这个点以及它连的边都删掉,再更新入队的元素就可以了~

#include<bits/stdc++.h>
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i))
#define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i))
#if defined(_MSC_VER) || __cplusplus > 199711L
#define aut(r,v) auto r = (v)
#else
#define aut(r,v) __typeof(v) r = (v)
#endif
#define each(it,o) for(aut(it, (o).begin()); it != (o).end(); ++ it)
#define all(o) (o).begin(), (o).end()
#define pb(x) push_back(x)
#define mp(x,y) make_pair((x),(y))
#define mset(m,v) memset(m,v,sizeof(m))
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef vector<int> vi; typedef pair<int, int> pii; typedef vector<pair<int, int> > vpii; typedef long long ll;
template<typename T, typename U> inline void amin(T &x, U y) { if(y < x) x = y; }
template<typename T, typename U> inline void amax(T &x, U y) { if(x < y) x = y; }

int main() {
    int T;
    scanf("%d", &T);
    for(int ii = 0; ii < T; ++ ii) {
        int n; int m; int k;
        scanf("%d%d%d", &n, &m, &k);
        vector<vector<int> > g(n);
        vi deg(n);
        for(int i = 0; i < m; ++ i) {
            int u, v;
            scanf("%d%d", &u, &v), -- u, -- v;
            g[u].push_back(v);
            ++ deg[v];
        }
        priority_queue<int,vi,greater<int> > q;
        rep(i, n) if(deg[i] <= k)
            q.push(i);
        vector<bool> used(n, false);
        ll anssum = 0;
        rep(t, n) {
            int i;
            while(1) {
                i = q.top(); q.pop();
                if(!used[i] && deg[i] <= k)
                    break;
            }
           // cout<<"i= "<<i<<endl;
            anssum += (ll)(t + 1) * (i + 1);
            k -= deg[i];
            used[i] = true;
            each(j, g[i]) if(!used[*j]) {
                if(-- deg[*j] <= k)
                    q.push(*j);
            }
        }
        printf("%d\n", (int)(anssum % 1000000007));
    }
    return 0;
}

线段树不过就是换一种数据结构来维护罢了,方法都是一样的~

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100010;
const int mo=1e9+7;
struct bian{
    int next,point;
}b[410000];
int num[N<<2],d[N],n,m,p[N],K,len,where,w;
void add(int k1,int k2){
    d[k2]++; b[++len]=(bian){p[k1],k2}; p[k1]=len;
}
void buildtree(int k1,int k2,int k3){
    if (k2==k3){
        num[k1]=d[k2]; return;
    }
    int mid=k2+k3>>1;
    buildtree(k1*2,k2,mid); buildtree(k1*2+1,mid+1,k3);
    num[k1]=min(num[k1*2],num[k1*2+1]);
}
int find(int k1,int k2,int k3){
//    cout<<"fa "<<k1<<" "<<num[k1]<<" "<<num[k1*2]<<" "<<k2<<" "<<k3<<" "<<K<<endl;
    if (k2==k3) return k2;
    int mid=k2+k3>>1;
    if (num[k1*2]<=K)
        return find(k1*2,k2,mid);
    else return find(k1*2+1,mid+1,k3);
}
void change(int k1,int k2,int k3){
    if (k2==k3){
        num[k1]=d[where]; return;
    }
    int mid=k2+k3>>1;
    if (mid>=where) change(k1*2,k2,mid); else change(k1*2+1,mid+1,k3);
    num[k1]=min(num[k1*2],num[k1*2+1]);
}
void solve(){
    scanf("%d%d%d",&n,&m,&K); len=0;
    for (int i=1;i<=n;i++) p[i]=0,d[i]=0;
    for (int i=1;i<=m;i++){
        int k1,k2; scanf("%d%d",&k1,&k2); add(k1,k2);
    }
    buildtree(1,1,n); int ans=0;
    for (int t=1;t<=n;t++){
        int now=find(1,1,n); ans=(ans+1ll*t*now)%mo;
    //    cout<<"faaa "<<now<<" "<<d[now]<<" "<<K<<endl;
        K-=d[now];
        for (int i=p[now];i;i=b[i].next){
            int j=b[i].point;
            d[j]--; where=j; change(1,1,n);
        }
        where=now; d[now]=1e9; change(1,1,n);
    }
    printf("%d\n",ans);
}
int main(){
    int t; scanf("%d",&t);
    for (;t;t--) solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值