9.2模拟题解

这次没传送门了……题目懒得copy上来。

T1.高维宇宙

这题很明显是二分图匹配,题目中明确说了要配对。

我一开始的建图方法是有问题的,因为违背了二分图的一个性质,就是在同一个点集中的点是互相不能连边的。那么如何解决这个问题呢?我们知道任何一个大于2的质数必然是奇数,所以我们可以把所有的奇数作为一个点集,所有的偶数作为一个点集,若两者之和为质数则连边,之后跑网络流即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
const int M = 10005;
const int INF = 1e9;
typedef long long ll;

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

struct node
{
    int from,to,next,v;
}e[M];
int n,a[105],ecnt = -1,maxflow,deep[105],head[105],prime[20005],tot,source,sink,cur[105];
int ji[105],ou[105],cnt1,cnt2;
bool np[20005];
queue <int> q;

void add(int x,int y,int z)
{
    e[++ecnt].to = y;
    e[ecnt].v = z;
    e[ecnt].from = x;
    e[ecnt].next = head[x];
    head[x] = ecnt;
}

void euler()
{
    np[1] = 1;
    rep(i,2,20000)
    {
        if(!np[i]) prime[++tot] = i;
        for(int j = 1;i * prime[j] <= 20000;j++)
        {
            np[prime[j]*i] = 1;
            if(!(i % prime[j])) break;
        }
    }
//    rep(i,1,30) printf("%d ",prime[i]);
}

bool bfs(int s,int t)
{
    memset(deep,-1,sizeof(deep));
    rep(i,0,sink+1) cur[i] = head[i];
    while(!q.empty()) q.pop();
    deep[s] = 0,q.push(s);
    while(!q.empty())
    {
        int k = q.front();q.pop();
        for(int i = head[k];i != -1;i = e[i].next)
        {
            if(deep[e[i].to] == -1 && e[i].v)
            deep[e[i].to] = deep[k] + 1,q.push(e[i].to);
        }
    }
    if(deep[t] == -1) return 0;
    else return 1;
}

int dfs(int s,int t,int limit)
{
    if(s == t || !limit) return limit;
    int flow = 0;
    for(int i = cur[s];i != -1;i = e[i].next)
    {
        cur[s] = i;
        if(deep[e[i].to] != deep[s] + 1) continue;
        int f = dfs(e[i].to,t,min(limit,e[i].v));
        if(f)
        {
            e[i].v -= f,e[i^1].v += f;
            flow += f,limit -= f;
            if(!limit) break;
        }
    }
    if(!flow) deep[s] = -2333333;
    return flow;
}

void dinic(int s,int t)
{
    while(bfs(s,t)) maxflow += dfs(s,t,INF);
}

int main()
{
//    freopen("prime.in","r",stdin);
//    freopen("prime.out","w",stdout);
    memset(head,-1,sizeof(head));
    n = read(),source = 0,sink = 101;
    rep(i,1,n) 
    {
        a[i] = read();
        if(a[i] & 1) ji[++cnt1] = a[i],add(source,cnt1,1),add(cnt1,source,0);
        else ou[++cnt2] = a[i],add(cnt2+n,sink,1),add(sink,cnt2+n,0);
    }
    euler();
    rep(i,1,cnt1)
        rep(j,1,cnt2)
            if(!np[ji[i] + ou[j]]) add(i,j+n,1),add(j+n,i,0);
    dinic(source,sink);
    printf("%d\n",maxflow);
    return 0;
}

T2.旅行

这题当时没想出来……只写了30pts宽搜暴力。

这道题的题目描述不是很清晰……既然pppfish要带领一群泡泡怪行走,所以肯定只走一条路径。而且题目都说了可能有多组解……显然不会走很多路径。

既然如此,我们知道所选择的答案区间肯定是连续的,而且左右端点必然是在边中出现过。所以我们可以选择枚举左端点,之后把所有的边按右端点从大到小排序,之后依次加入,注意加入的边必须合法,也就是当前枚举的左端点必然在这条边的l,r之内才能被加入。每加入一次用并查集判断图是否联通,如联通更新即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
const int M = 10005;
const int INF = 1e9;
typedef long long ll;

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

struct edge
{
    int from,to,next,dl,dr;
    bool operator < (const edge &g) const
    {
        return dr > g.dr;
    }
}e[M];

int n,m,a,b,l,r,ecnt,head[M],maxn,pos = INF,fa[M],kl,ans,kr;
bool vis[M];

void add(int x,int y,int l,int r)
{
    e[++ecnt].to = y;
    e[ecnt].dl = l;
    e[ecnt].dr = r;
    e[ecnt].from = x;
    e[ecnt].next = head[x];
    head[x] = ecnt;
}

int getfa(int x)
{
    if(fa[x] == x) return fa[x];
    else return fa[x] = getfa(fa[x]);
}

bool check()
{
    int r1 = getfa(1),r2 = getfa(n);
    if(r1 == r2) return 1;
    else return 0;
}

int main()
{
    n = read(),m = read();
    rep(i,1,m) a = read(),b = read(),l = read(),r = read(),add(a,b,l,r);
    sort(e+1,e+1+m);
    rep(i,1,m)
    {
        kl = e[i].dl,kr = e[i].dr;
        rep(j,1,n) fa[j] = j;    
        int r1 = getfa(e[i].from),r2 = getfa(e[i].to);
        if(r1 != r2) fa[r1] = r2;
        rep(j,1,m)
        {
            if(kl < e[j].dl || kl > e[j].dr) continue;
            int r1 = getfa(e[j].from),r2 = getfa(e[j].to);
            if(r1 != r2) fa[r1] = r2;
            if(check())
            {
                kr = min(kr,e[j].dr);
                int d = kr - kl + 1;
                if(d > maxn) maxn = d,pos = kl;
                else if(d == maxn) pos = min(pos,kl);
                break;
            }
        }
    }
    printf("%d\n",maxn);
    rep(i,pos,pos+maxn-1) printf("%d ",i);enter;
    return 0;
}

T3.字典

这题本来是会写的……然而考试的时候没时间写跪了,惨淡爆零。

其实正解很好想,我们要求一个字符串是哪几个字符串的前缀,很容易就想到先建一棵trie树,之后的问题在于,我们怎么维护每个字符串的所得到的答案中的最长全0串。其实我们发现一个0串的长度相当于这个字符串所出现过的相邻两个字符串的编号之差。所以我们可以在建树的时候动态维护差值最大值,之后再访问的时候更新即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
const int M = 505;
typedef long long ll;

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

int cnt = 1,n,m;
char s[5000005];
struct trie
{
    int fir,la,p,v;
}c[5000005][3];

void insert(int num)
{ 
    int len = strlen(s),now = 1;
    rep(i,0,len-1)
    {
        int k = s[i] - 'a';
        if(!c[now][k].v) c[now][k].v = ++cnt;
        c[now][k].p = max(c[now][k].p,num-c[now][k].la-1);
        c[now][k].la = num;
        now = c[now][k].v;
    }
}

int query()
{
    bool flag = 0;
    int len = strlen(s),now = 1,ans;
    rep(i,0,len-1)
    {
        int k = s[i] - 'a';
        if(!c[now][k].v) return n;
        if(i == len-1) return max(c[now][k].p,n - c[now][k].la);
        now = c[now][k].v;
    }
}

int main()
{
    n = read(),m = read();
    rep(i,1,n) scanf("%s",s),insert(i);
    rep(i,1,m) scanf("%s",s),printf("%d\n",query());
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/captain1/p/9576354.html