Codeforces Round #533 (Div. 2)

Codeforces Round #533 (Div. 2)题解

A

题意:
给你一个序列,你可以修改序列中的每个数使其变成一个另一个正整数,如a-->b代价为abs(a-b)
求一个t是的修改后的(abs)(a[i]-t)<=1,求最小代价
题解:
因为a很小枚举即可

#include<bits/stdc++.h>
using namespace std;
int a[1005];
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1; i<=n; i++) scanf("%d",&a[i]);
    int ans1=2000000000,ans2=a[n];
    for (int t=1; t<=100; t++)
    {
        int sum=0;
        for (int j=1; j<=n; j++)
        {
            int zz=2000000000;
            for (int tt=t-1; tt<=t+1; tt++) if (tt>0)
            {
                zz=min(zz,abs(tt-a[j]));
            }
            sum+=zz;
        }
        if (sum<ans1)
        {
            ans1=sum; ans2=t;
        }
    }
    printf("%d %d\n",ans2,ans1);
}

B

题意:
给一个字符串和k,连续k个相同的字符,可使等级x加1,
例:8 2 aaacaabb 则有aa aa 即x=2。 求最大的x
题解:
O(N)扫一遍,对于每一段统计一下答案

#include<bits/stdc++.h>
using namespace std;
int n,k;
char s[200005];
int sum[10000];
int main()
{
    scanf("%d%d",&n,&k);
    scanf("%s",s+1);
    int pre=0,ans=0,len=0;s[n+1]='A'-1;
    for (int i=1; i<=n+1; i++)
    {
        int x=s[i]-'A'+1;
        if (x!=pre)
        {

            sum[pre]+=len/k;
            //cout<<x<<" "<<sum[x]<<" "<<len/k<<endl;
            ans=max(ans,sum[pre]);
            len=1;
        }
        else len++;
        pre=x;
    }

    printf("%d\n",ans);
    return 0;
}

C

题意:
一个n长的数组每个位置可以填区间l-r的值。
有多少种填法,使得数组每个位置相加的和是3的倍数
题解:
统计一下l到r mod 3为0、1、2的个数 之后DP一下就可以了

#include<bits/stdc++.h>
#define N 200005
#define mod 1000000007
using namespace std;
long long  f[N][4];
int n,l,r;
long long a[4];
int main()
{
    scanf("%d%d%d",&n,&l,&r);
    a[0]=a[1]=a[2]=(r-l+1)/3;
    int xx=(r-l+1-a[0]*3);
    if (xx==1)
    {
        if (r%3==0) a[0]++;
        if (r%3==2) a[2]++;
        if (r%3==1) a[1]++;
    }
    else if (xx==2)
    {
        a[0]++; a[1]++; a[2]++;
        if (r%3==0) a[1]--;
        if (r%3==2) a[0]--;
        if (r%3==1) a[2]--;
    }
   // cout<<" "<<a[0]<<" "<<a[1]<<" "<<a[2]<<endl;
    f[1][1]=a[1];
    f[1][2]=a[2];
    f[1][0]=a[0];
    for (int i=2; i<=n; i++)
    {
        f[i][0]=((f[i-1][0]*a[0]%mod+f[i-1][1]*a[2]%mod)%mod+f[i-1][2]*a[1]%mod)%mod;
        f[i][1]=((f[i-1][0]*a[1]%mod+f[i-1][1]*a[0]%mod)%mod+f[i-1][2]*a[2]%mod)%mod;
        f[i][2]=((f[i-1][0]*a[2]%mod+f[i-1][1]*a[1]%mod)%mod+f[i-1][2]*a[0]%mod)%mod;

    }
    printf("%lld",f[n][0]);
    return 0;
}

D

题意:
给n*m的地图,最多9个人,同时有每个人的扩张次数(我开始以为是直线扩张最大长度。。实际是能连续扩张次数。)
地图上有‘#’,‘.',和数字,数字对应每个人的据点,
从1-n轮流扩张。
地图被扩张完后,输入每个人的据点数目。
题解:
开p个队列,模拟一下就可以了

#include<bits/stdc++.h>
using namespace std;
int n,m,p;
int dx[5]={0,0,-1,1},dy[5]={1,-1,0,0};
struct Node{
    int x,y;
};
queue<Node> q[20];
int ans[20],speed[20];
char s[1005][1005];
void run(int i)
{
    for (int tt=q[i].size()-1; tt>=0; tt--)
    {
        Node nownode=q[i].front(); ans[i]++;
        q[i].pop();
        for (int k=0; k<4; k++)
        {
            int xx=nownode.x+dx[k],yy=nownode.y+dy[k];
            if (xx<1 || xx>n || yy<1 || yy>m) continue;
            if (s[xx][yy]=='.')
            {
                s[xx][yy]='0'+i;
                Node newnode={xx,yy};
                q[i].push(newnode);
            }
        }
    }
}
void init()
{
    scanf("%d%d%d",&n,&m,&p);
    for (int i=1; i<=p; i++) scanf("%d",&speed[i]);
    for (int i=1; i<=n; i++)
    {
        scanf("%s",s[i]+1);
        for (int j=1; j<=m; j++)
        {
            if (s[i][j]=='.' || s[i][j]=='#') continue;
            int X=s[i][j]-'1'+1;
            Node newnode={i,j};
            q[X].push(newnode);
        }
    }
}
int main()
{
    init();
    while (true)
    {
        for (int i=1; i<=p; i++)
        {
            for (int j=1;  (!q[i].empty() && j<=speed[i]);  j++) run(i);
        }
        int sum=0;
        for (int i=1; i<=p; i++) sum+=q[i].size();
        //cout<<sum<<endl;
        if (sum==0) break;
    }
    for (int i=1; i<=p; i++) printf("%d ",ans[i]);
}

E

题意:
有 n 个事件,op = 1 表示我可以修改昵称,op = 2 表示一个名为 s_i 的朋友查询我当前的名字。一个朋友是高兴的当且仅当他每次查询我的名字都为 s_i,保证每个朋友至少查询一次我的名字,问最多可以有多少个朋友高兴。

题解:
之前以为只要高兴一次就可以了 后面才发现每次高兴才算高兴

只高兴一次就算高兴的话 可以用网络流写
1.对于每次修改账号的点 从S连一条容量为1的边
2.对于每次查询,从之前的修改向其连一条容量为1的边
3.对于每个人向T连一条容量为1的边

#include<bits/stdc++.h>
#define N 1005
#define INF 100000000
using namespace std;
int n,m;
int ans;
int tot1,tot2;
map<string,int> mp;
struct Edge{
    int from,to,cap,flow,pre;
};
struct Dinic{
    int n,m,s,t,edgenumber;
    int now[N],dis[N];
    bool vis[N];
    vector<Edge> edges;
    void preclear()
    {
        edgenumber=0;
        edges.clear();
        memset(now,-1,sizeof(now));
    }
   void AddEdge(int from,int to,int cap)
   {
      // cout<<"           "<<from<<" "<<to<<" "<<cap<<endl;
        Edge E1={from,to,cap,0,now[from]};
        edges.push_back(E1);
        now[from]=edgenumber++;
        Edge E2={to,from,0,0,now[to]};
        edges.push_back(E2);
        now[to]=edgenumber++;
    }
    bool BFS()
    {
        queue<int> Q;
        memset(vis,0,sizeof(vis));
        memset(dis,-1,sizeof(dis));
        Q.push(s); vis[s]=1;
        while (!Q.empty())
        {
            int x=Q.front(); Q.pop();
            //cout<<x<<endl;
            for (int p=now[x]; p!=-1; p=edges[p].pre)
            {
                Edge e=edges[p];

                if (!vis[e.to] && e.cap>e.flow)
                {
                    vis[e.to]=1; dis[e.to]=dis[x]+1;
                    Q.push(e.to);
                }
            }
        }
        return vis[t];
    }
    int dfs(int x,int a)
    {
        if (x==t || a==0)
        {
            return a;
        }
        int flow=0,f;
        for (int p=now[x]; p!=-1; p=edges[p].pre)
        {
            Edge e=edges[p];
            if (dis[e.to]==dis[x]+1 && (f=dfs(e.to,min(a,e.cap-e.flow))) >0)
            {
                edges[p].flow+=f;
                edges[p^1].flow-=f;
                a-=f;
                flow+=f;
                if (a==0) break;
            }
        }
        return flow;
    }
    int maxflow(int s,int t)
    {
        this -> s=s;
        this -> t=t;
        int flow=0;
        while (BFS())
        {
            //cout<<"shit"<<endl;
            flow+=dfs(s,INF);
        }
        return flow;
    }
}D;

int main()
{
    D.preclear();
    scanf("%d%d",&n,&m);
    int pre=-1,prex=0,s,t; tot1=1;
    s=0;    t=1;
    for (int i=1; i<=n; i++)
    {
        int type;
        scanf("%d",&type);
        if (type==1)
        {
            ++tot1;
            pre=tot1;
            prex=1;
        }
        else
        {
           string ss;
            if (prex==1)
            D.AddEdge(s,pre,1);
           cin>>ss;
           if (mp.find(ss)==mp.end())
           {
               mp[ss]=++tot1;

               D.AddEdge(mp[ss],t,1);
           }
           if (pre!=-1) D.AddEdge(pre,mp[ss],1);
           prex=2;
        }
    }
    cout<<D.maxflow(s,t)<<endl;
    return 0;
}

每次高兴才算高兴的话
其实就是对于一段连续的查询操作统计答案时最多只能统计一次
所以对于一段连续的查询操作中任意两点连边所形成的图的 补集的最大团就是答案
之后利用最大团的随机算法就好
至于随机算法证明我就只能告诉你应该和概率相关,具体不会证明

#include<bits/stdc++.h>
using namespace std;
int n,m,k,a[50],b[50],tot;
bool vis[50][50];
string ss;
map<string,int>mp;
bitset<50>s,ans;
void work1()
{
    for (int i=1; i<=a[0]; i++)
    {
        for (int j=i+1; j<=a[0]; j++)
            vis[a[i]][a[j]]=vis[a[j]][a[i]]=false;
    }
    a[0]=0;
}
void work2()
{
    if (!mp.count(ss)) mp[ss]=++tot;
    a[++a[0]]=mp[ss];
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(vis,true,sizeof(vis));
    for (int i=1; i<=n; i++)
    {
        int type;
        scanf("%d",&type);
        if (type==1) work1();
        else cin>>ss,work2();
    }
    work1();
    int T=5e4;
    for (int i=1; i<=m; i++) b[i]=i;
    while (T--)
    {
        random_shuffle(b+1,b+m+1);
        for (int i=1; i<=m; i++) s[i]=1;
        for (int i=1; i<=m; i++)
        {
            if (s[b[i]]==1)
            {
                for (int j=i+1; j<=m; j++)
                {
                    if (!vis[b[i]][b[j]]) s[b[j]]=0;
                }
            }
        }
        if (s.count()>ans.count()) ans=s;
    }
    cout<<ans.count()<<endl;
}
posted @ 2019-01-21 14:11  ACist  阅读(198)  评论(0编辑  收藏  举报