2014-2015 ACM-ICPC Northeastern European Regional Contest (NEERC 14) 解题报告

Preface

http://codeforces.com/gym/100553

A Alter Board

题意:给定一个黑白相间的n*m矩阵,每次可以选择一个矩形,将其中的颜色的进行反转。问最少多少次操作可以将矩阵变为纯色。
分析:先反转所有偶数行,再反转所有偶数列即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>

using namespace std;
int x[11000],y[11000];
int ans;
int main()
{
    freopen("alter.in","r",stdin);
    freopen("alter.out","w",stdout);
    int n,m;
    cin>>n>>m;
    ans=(n/2)+(m/2);
    cout<<ans<<endl;
    for (int i=1;i<=n/2;i++)
        cout<<i*2<<" "<<1<<" "<<i*2<<" "<<m<<endl;
    for (int i=1;i<=m/2;i++)
        cout<<1<<" "<<2*i<<" "<<n<<" "<<2*i<<endl;
    return 0;
}

B Burrito King

题意:餐厅里有道菜,由n种原料构成,每种原料最多为g[i],定义Albert的满意度为

i=1nsiai

Barney的不满意度为
i=1nsibi

现在要求安排一种原料的选取方案(实数),使得Barney的不满意度不超过B并且,Albert的满意度达到最高。
分析:贪心,我们按照 aibi 从大到小排序,能选取多少选取多少,这样能达到最优。因为这类似于一种背包,背包的容量为B,物品的价值为 aibi ,数量为g[i]。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>

using namespace std;
struct node
{
    double k;
    int id;
}a[1100000];
int n,num;
const double eps=1e-12;
int A,B;
int g[1100000],x[1100000],y[1100000];
double ans[1100000];
int cmp(node a,node b)
{
    return a.k>b.k;
}

double maxx,minn;
int main()
{
    freopen("burrito.in","r",stdin);
    freopen("burrito.out","w",stdout);
    scanf("%d %d %d",&n,&A,&B);
    for (int i=1;i<=n;i++)
    {
        scanf("%d %d %d",&g[i],&x[i],&y[i]);
        if (x[i]==0) a[i].k=0;
        else if (y[i]==0) a[i].k=1e8;
        else
        {
            a[i].k=(double)x[i]/y[i];
        }
        a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    for (int i=1;i<=n;i++)
    {
        if (y[a[i].id]==0) ans[a[i].id]=g[a[i].id];
        else if (x[a[i].id]==0) ans[a[i].id]=0;
        else
        {
            double d=g[a[i].id];
            d=min(d,(double)(B-minn)/(double)(y[a[i].id]));
            ans[a[i].id]=d;
        }
        maxx+=ans[a[i].id]*(double)x[a[i].id];
        minn+=ans[a[i].id]*(double)y[a[i].id];
    }
    if (maxx+eps<A||minn-eps>B) printf("-1 -1\n");
    else
    {
        printf("%.10f %.10f\n",maxx,minn);
        for (int i=1;i<=n;i++)
        {
            printf("%.10f",ans[i]);
            if (i!=n) printf(" ");
            else printf("\n");
        }
    }
    return 0;
}

C Cactus Generator

D Damage Assessment

E Epic Win!

F Filter

题意:题目描述比较繁琐,简单说就是给你一个长度小于250的16进制序列。给你一些hash函数f的系数 ai 。如果一个16进制序列蕴含用户 uk 的消息,当且仅当,对于任意的hash函数,统计公式计算:

j=(aiuk)modm

的任意一个j,16进制中第j位必定是1。给定n个用户的 uk ,和q个16进制序列,问那些16进制序列蕴含至少一个用户的信息。
分析:利用bitset模拟这个过程即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>

using namespace std;
int n,m;
bitset<1100> bv;
int a[1100];
int u[1100];
int cal(char x)
{
    if (x>='0'&&x<='9') return x-'0';
    else return x-'a'+10;
}
int q,k;
int ans,way[1100];
string s[1100];
int main()
{
    freopen("filter.in","r",stdin);
    freopen("filter.out","w",stdout);
    cin>>m>>n;
    for (int i=1;i<=n;i++)
        cin>>a[i];
    cin>>q;
    for (int i=0;i<q;i++)
    {
        cin>>s[i];
    }
    cin>>k;
    for (int i=1;i<=k;i++)
        cin>>u[i];
    for (int i=0;i<q;i++)
    {
        int l=s[i].size();
        string ss="";
        int now=0;
        for (int kk=0;kk<m/4;kk++)
        {
            int x=cal((char)s[i][kk]);
            for (int ll=1;ll<=4;ll++)
                ss+=(char)(x%2+'0'),x/=2;
        }
        if (m%4)
        {
            int x=cal((char)s[i][l-1]);
            for (int ll=0;ll<m%4;ll++)
                ss+=(char)(x%2+'0'),x/=2;
        }
        //cout<<ss<<endl;
        bitset<1100> bv(ss);
        for (int j=1;j<=k;j++)
        {
            int flag=0;
            for (int ll=1;ll<=n;ll++)
                if (!bv[m-1-((u[j]%m)*(a[ll]%m))%m])
                {
                    flag=1;
                    break;
                }
            if (!flag)
            {
                ans++;
                way[ans]=i;
                break;
            }
        }
    }
    printf("%d",ans);
    for (int i=1;i<=ans;i++)
        printf(" %d",way[i]);
    printf("\n");
    return 0;
}

G Gomoku

H Hidden Maze

I Improvements

题意:有n个飞船,和一个空间站(无论何时,飞船都在空间站的同侧),每个飞船有一个位置 xi ,空间站在0号位置,第i个飞船有一条绳,将其与第i-1个飞船连接在一起,1号飞船与空间站相连。我们可以每次将一个飞船移动到任意位置。问,最多保留多少个飞船处于原位置,使得任意两条绳子不相交(包含不算)。
分析:按照昂神的题解,此题的答案等于飞船从前往后的标号序列,拥有的最长的先递增后递减的子序列的长度。之后我们可以nlogn前后扫两边最长上升子序列长度即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>

using namespace std;
struct node
{
    int x,id;
}a[210000];
int n;
int cmp(node a,node b)
{
    return a.x<b.x;
}
struct data{
   int l,r;
   int maxx;
}tr[2000001];
int flag=0,pos;
void build(int k,int s,int t)
{
    tr[k].l=s;tr[k].r=t;
    if(s==t)
    {
        tr[k].maxx=0;       
        return;
    }
    int mid=(s+t)>>1;
    build(k<<1,s,mid);
    build(k<<1|1,mid+1,t);
    tr[k].maxx=max(tr[k<<1].maxx,tr[k<<1|1].maxx);
}
void change(int k,int a,int c)
{
    int l=tr[k].l,r=tr[k].r;
    if(r==l)
    {
        tr[k].maxx=max(tr[k].maxx,c);
        return;
    } 
    int mid=(l+r)>>1;
    if (a<=mid) change(k<<1,a,c);
    else change(k<<1|1,a,c);
    tr[k].maxx=max(tr[k<<1].maxx,tr[k<<1|1].maxx);
}
int ask(int k,int a,int b)
{
    int l=tr[k].l,r=tr[k].r;
    if(a==l&&b==r)
    {
        return tr[k].maxx;
    } 
    int mid=(l+r)>>1;
    if(b<=mid) return ask(k<<1,a,b);
    else if(a>mid) return ask(k<<1|1,a,b);
    else return max(ask(k<<1,a,mid),ask(k<<1|1,mid+1,b));
}
int l[210000],r[210000];
int main()
{
    freopen("improvements.in","r",stdin);
    freopen("improvements.out","w",stdout);
    cin>>n;
    for (int i=1;i<=n;i++)
    {
        cin>>a[i].x,a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    build(1,1,n);
    for (int i=1;i<=n;i++)
    {
        if (a[i].id!=1)
        {
            int maxx=ask(1,1,a[i].id-1);
            l[i]=maxx+1;
            change(1,a[i].id,l[i]);
        }
        else l[i]=1,change(1,a[i].id,1);
    }
    build(1,1,n);
    for (int i=n;i>=1;i--)
    {
        if (a[i].id!=1)
        {
            int maxx=ask(1,1,a[i].id-1);
            r[i]=maxx+1;
            change(1,a[i].id,r[i]);
        }
        else r[i]=1,change(1,a[i].id,1);
    }
    int maxx=0;
    for (int i=1;i<=n;i++)
        maxx=max(maxx,l[i]+r[i]-1);
    cout<<maxx<<endl;
    return 0;
}

J Jokewithpermutation

题意:给定一个n小于等于50的全排列删去中间空格后的字符串序列。要求让还原成原来的全排列形式。
分析:先判断出n的大小,然后dfs搜索一下即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>

using namespace std;
int pp[51];
int way[51];
string s;
int l,n;
int flag;
void dfs(int pos,int num)
{
    if (pos==l)
    {
        flag=1;
        for (int i=1;i<=n;i++)
        {
            printf("%d",way[i]);
            if (i!=n) printf(" ");
            else printf("\n");
        }
        return ;
    }
    if (!pp[s[pos]-'0']&&s[pos]-'0'<=n)
    {
        pp[s[pos]-'0']=1;
        way[num]=s[pos]-'0';
        dfs(pos+1,num+1);
        pp[s[pos]-'0']=0;
        if (flag) return;
    }
    if (pos+1<l&&!pp[(s[pos]-'0')*10+s[pos+1]-'0']&&(s[pos]-'0')*10+s[pos+1]-'0'<=n)
    {
        pp[(s[pos]-'0')*10+s[pos+1]-'0']=1;
        way[num]=(s[pos]-'0')*10+s[pos+1]-'0';
        dfs(pos+2,num+1);
        pp[(s[pos]-'0')*10+s[pos+1]-'0']=0;
        if (flag) return;
    }
}
int now;
int main()
{
    freopen("joke.in","r",stdin);
    freopen("joke.out","w",stdout);
    pp[0]=1;
    cin>>s;
    l=s.size();
    for (int i=1;i<=50;i++)
    {
        n++;
        if (i<10) now+=1;
        else now+=2;
        if (now==l) break;
    }
    dfs(0,1);
    return 0;
}

K Knockout Racing

题意:有n(<=1000)辆汽车在 ai bi 的区间内周期往复。有m(<=1000)个询问,问在t时刻,在 li ri 区间内有多少辆汽车。
分析:模拟下就行。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<vector>

using namespace std;
int n,m;
int l[1100],r[1100],d[1100];
int t,ll,rr;
int cal(int x,int t)
{
    t%=d[x];
    if (t<=r[x]-l[x])
    {
        return l[x]+t;
    }
    else 
    {
        t-=(r[x]-l[x]);
        return r[x]-t;
    }
}
int main()
{
    freopen("knockout.in","r",stdin);
    freopen("knockout.out","w",stdout);
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        cin>>l[i]>>r[i];
        d[i]=(r[i]-l[i])*2;
    }
    for (int i=1;i<=m;i++)
    {
        cin>>ll>>rr>>t;
        int num=0;
        for (int i=1;i<=n;i++)
        {
            int pos=cal(i,t);
            //cout<<pos<<endl;
            if (pos>=ll&&pos<=rr) num++;
        }
        cout<<num<<endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值