DCPC 2015 三星题(10/10)

总结:都是水题. 难度在DIV2 C以下.

Problem B

水题.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e2+5,inf=0x3f3f3f3f;
int main()
{
    int T,n;
    cin>>T;
    while(T--)
    {
        int lx=inf,rx=-inf,uy=inf,dy=-inf;
        int x;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            for(int j=1;j<=8;j++)
            {
                scanf("%d",&x);
                if(j%2)
                    lx=min(lx,x),rx=max(rx,x);
                else
                    uy=min(uy,x),dy=max(dy,x);
            }
        }
        cout<<(rx-lx)*(dy-uy)<<endl;
    }
    return 0;
}


Problem F 暴力

题意:给出两个串s1,s2 每次从其中一个串的开头选出一个字符 选n+m次结束.
要求:把生成不同的串从小到大输出,n,m<=8.

直接dfs暴力即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e2+5;
char a[N],b[N];
vector<string> v;
int n,m;
void dfs(int x,int y,string s)
{
    if(x==n&&y==m)
    {
        v.push_back(s);
        return;
    }
    if(x<n)
        dfs(x+1,y,s+a[x]);
    if(y<m)
        dfs(x,y+1,s+b[y]);
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        v.clear();
        scanf("%s%s",a,b);
        n=strlen(a),m=strlen(b);
        dfs(0,0,"\0");
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        for(int i=0;i<v.size();i++)
            cout<<v[i]<<endl;
        cout<<endl;
    }
    return 0;
}

Problem J 暴力打表
题意:给出数b.问第一个比b大的素数并且其二进制为回文的数为多少? b<=2^21.
先求出2e6内的素数,然后O(sz*20)生成是回文的素数表. 在表中找到第一个比b大的即可.


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+5,inf=0x3f3f3f3f,M=3e6;
bool vis[M+5];
int pri[M+5],pn=0;
vector<int> a;
bool check(int x)
{
    int b[30],sz=0;
    while(x)
        b[++sz]=x%2,x/=2;
    for(int i=1;i<=sz/2;i++)
        if(b[i]!=b[sz-i+1])
            return false;
    return true;
}
void init()
{
    for(int i=2;i<=M;i++)
    {
        if(!vis[i])
        {
            pri[pn++]=i;
            for(int j=i+i;j<=M;j+=i)
                vis[j]=1;
        }
    }
   // cout<<pn<<endl;
    for(int i=0;i<pn;i++)
    {
        if(check(pri[i]))
            a.push_back(pri[i]);
    }
}
void fun(int x)
{
    int b[30],sz=0;
    while(x)
        b[++sz]=x%2,x/=2;
    for(int i=sz;i>=1;i--)
        printf("%d",b[i]);
    printf("\n");
}
char b[30];
int main()
{
    int T,n;
    init();
    while(scanf("%s",b)!=EOF)
    {
        int c=0;
        for(int i=0;b[i];i++)
            c=c*2+b[i]-'0';
        int pos=lower_bound(a.begin(),a.end(),c)-a.begin();
        fun(a[pos]);
    }
    return 0;
}

Problem G 暴力
题意:题意:知道两个串s1,s2的长度为n,m 每次从其中一个串的开头选出一个字符 选n+m次结束.
问总共能生成多少个串(可以有相同的) n,m<=1e4.

暴力 f[n][m]为长度为n,m的串能生成多少个串 f[n][m]=f[n-1][m]+f[n][m-1].

正解: n+m的串 把第一个串随机放入n+m个位置中,剩下m个位置第二个串的元素依次填入即可. C(n+m,n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+2,inf=0x3f3f3f3f,mod=1e9+7;
int f[N][N];
void init()
{
    f[0][0]=1;
    for(int i=1;i<N;i++)
        f[i][0]=f[0][i]=1;
    for(int i=1;i<N;i++)
    {
        for(int j=1;j<N;j++)
           f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
    }
}
int main()
{
    int T;
    init();
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        printf("%d\n",f[n][m]);
    }
    return 0;
}

Problem B 贪心暴力
题意:n个硬币,第i个硬币价值v[i].选硬币:价值为x的硬币要么不选 要么全选.
当选的总钱数>=M时 最少需要多少种类硬币.(最小数量相同输出总钱数大的方案.)
n,v[i]<=1e6.M<=1e9.

首先种类要求最少.二元组F(a,b)选种类b能获得的钱a,从大到小排序即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=1e6+2,inf=0x3f3f3f3f,mod=1e9+7;
ll b[N];
vector<ii> a;
vector<int> ans;
bool cmp(ii a,ii b)
{
    if(a.first==b.first)
        return a.second>b.second;
    return a.first>b.first;
}
int main()
{
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        ll sum=0;
        int x;
        memset(b,0,sizeof(b));
        ans.clear();
        a.clear();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&x),b[x]++,sum+=x;
        if(sum<m)
        {
            printf("Impossible\n");
            continue;
        }
        for(int i=1;i<N;i++)
        {
            if(b[i])
                a.push_back(ii(i*b[i],i));
        }
        sort(a.begin(),a.end(),cmp);
        ll res=0;
        for(int i=0;i<a.size();i++)
        {
            res+=a[i].first;
            ans.push_back(a[i].second);
            if(res>=m)
                break;
        }
        sort(ans.begin(),ans.end());
        for(int i=0;i<ans.size();i++)
            printf("%d ",ans[i]);
        printf("\n");
    }
    return 0;
}

Problem D 二分 最大值最小化
题意:给出长度为n的序列a,一个区间的价值为:该区间的最大值*该区间长度.
问把序列a分成k个区间 其区间最大值最小为多少? n,a[i]<=1e6,k<=1e5,

最大值最小化 二分答案,O(N)判定是否能分成<=k个区间即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=1e6+2,inf=0x3f3f3f3f,mod=1e9+7;
int n,k;
ll a[N];
bool check(ll lim)
{
    ll cnt=0;
    int r=1;
    for(int l=1;l<=n;l=r)
    {
        ll mx=0;
        while(r<=n)
        {
            mx=max(mx,a[r]);
            ll len=r-l+1;
            //if(lim==10)
             //   cout<<l<<' '<<r<<' '<<len*mx<<endl;
            if(len*mx>lim)
                break;

            r++;
        }
        cnt++;
    }
    return cnt<=k;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll mx=0;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%I64d",&a[i]),mx=max(mx,a[i]);
        ll l=mx,r=2e16;
        while(l<=r)
        {
            ll m=l+r>>1;
            if(check(m))
                r=m-1;
            else
                l=m+1;
        }
        printf("%I64d\n",r+1);
    }
    return 0;
}

Problem E 01-Tire水
题意:长度为n的序列a,Q次询问
每次询问给出一个数x,找到a[p]^x > a[i]^x(i=1..n) 若有多个a[p] 输出p下标最小的. 
n,Q<=3e5,a[i]<=1e9.

对于询问,也就是在序列a中找到p使得a[p]^x最大的即可,Trie从高位走到低位即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=2e5+2,inf=0x3f3f3f3f,mod=1e9+7,M=33;
int ch[N*33][2],sz,num[N*33];
map<ll,ll> pos;
void init()
{
    sz=1;
    memset(ch[0],0,sizeof(ch[0]));
}
void insert(ll x)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int c=(x>>i)&1;
        if(!ch[u][c])
        {
            memset(ch[sz],0,sizeof(ch[sz]));
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    num[u]=x;
}
void query(ll x)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int c=(x>>i)&1;
        if(ch[u][c^1])
            u=ch[u][c^1];
        else
            u=ch[u][c];
    }
    printf("%d\n",pos[num[u]]);
}
int main()
{
    int T,n,Q;
    ll x;
    scanf("%d",&T);
    while(T--)
    {
        init();
        pos.clear();
        scanf("%d%d",&n,&Q);
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d",&x);
            if(!pos[x])
                pos[x]=i;
            insert(x);
        }
        while(Q--)
        {
            scanf("%d",&x);
            query(x);
        }
        printf("\n");
    }
    return 0;
}



Problem H
题意:给出n,(n1,c1),(n2,c2).<=2e9.
找到一对(x,y)满足 n[1]*x+n[2]*y=n && c[1]*x+c[2]*y 尽量小.


由exgcd: ax+by=d d|gcd(a,b) 
容易求出(x,y)的通解, x=x1+kb,y=y1-ka
如何满足c[1]*x+c[2]*y 尽量小.?

暴力遍历通解 水过了....

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,int> ii;
const int N=2e5+2,inf=0x3f3f3f3f,mod=1e9+7,M=33;
ll a,b,c1,c2,n,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    int r=exgcd(b,a%b,x,y),t;

    t=x,x=y,y=t-a/b*y;
    return r;
}
int main()
{
    while(cin>>n&&n)
    {
        scanf("%I64d%I64d%I64d%I64d",&c1,&a,&c2,&b);
        ll d=exgcd(a,b,x,y);
        if(n%d)
        {
            printf("failed\n");
            continue;
        }
        x=x*n/d;
        y=y*n/d;
        ll tx=x;
        x=((x%b)+b)%b;
        ll k=(tx-x)/b;
        y+=k*a;
        ll res=x*c1+y*c2;
        
        
        ll px=x,py=y;
        ll rx=x,ry=y;
        for(ll k=0;k<=1000;k++)
        {
            x=px+k*b;
            y=py-k*a;
            if(x>=0&&y>=0)
            {
                if(x*c1+y*c2<res)
                    res=x*c1+y*c2,rx=x,ry=y;
            }
            else
                break;
        }
        printf("%I64d %I64d\n",rx,ry);
    }
    return 0;
}

Problem I
题意:有n个传送站(x,y,r) 每个传送站可以传送到它半径r范围内的任意一个传送站.需要时间2s. 
人可以上下左右走单位1的距离.或着到传送站传送
起点为(sx,sy),终点为(ex,ey) -1e9<=x,y<=1e9,0<=r<=1e9 n<=1e2.问从起到到终点需要的最短时间.


把起点和终点也看成传送站,设d[i][j] 表示第i个传送站到第j个传送站需要的时间(中间节点编号为0).
根据半径和坐标建图.跑一遍Floyd(或者SPFA)即可 注意起点终点不能传送.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+5;
const double eps=1e-10;
struct node{
    double x,y;
    double r;
}a[N];
int n;
ll d[N][N];
double dis(int i,int j)
{
    double x1=a[i].x,y1=a[i].y;
    double x2=a[j].x,y2=a[j].y;
    return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
void floyd()
{
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
            }
        }
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lf%lf%lf",&a[i].x,&a[i].y,&a[i].r);
        ++n;
        scanf("%lf%lf",&a[n].x,&a[n].y),a[n].r=0;
        ++n;
        scanf("%lf%lf",&a[n].x,&a[n].y),a[n].r=0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                d[i][j]=abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y);
                if(dis(i,j)<=a[i].r&&i!=n-1&&i!=n&&j!=n&&j!=n-1)
                   d[i][j]=min(d[i][j],2ll);
            }
        }
        floyd();
        printf("%I64d\n",d[n-1][n]);
    }
    return 0;
}

Problem A
题意:n人比赛.n-1轮:每一轮从剩下的人当中,随机选出两个人进行比赛,输的人淘汰.
已知a[i][j] 第i个人打赢第j个人的概率. 
n<=20.问第i个人成为冠军的概率(i=[1..n]).


n<=20 暴力? 好像做不了. 状压? 把淘汰的人看成1,则第i个人为冠军的状态为11110111.
转移状态.知道状态后枚举下一个被淘汰的人x 在枚举x被谁淘汰,在乘以选这两个人概率即可. O(n^2 * 2^n).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=21;
double a[N][N],f[1<<N];
int main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%lf",&a[i][j]);
        memset(f,0,sizeof(f));
        f[0]=1;
        for(int i=0;i<(1<<n);i++)
        {
            double cnt=0;
            for(int k=0;k<n;k++)
                if(!((i>>k)&1))
                    cnt++;
            cnt=(cnt*(cnt-1))/2.0;
            for(int j=0;j<n;j++)
            {
                if((i>>j)&1)
                    continue;
                for(int k=0;k<n;k++)
                {
                    if(k==j)
                        continue;
                    if((i>>k)&1)
                        continue;
                    f[i|(1<<j)]+=f[i]*a[k][j]*1.0/cnt;
                }
            }
        }
        printf("Case %d:",++cas);
        for(int i=0;i<n;i++)
            printf(" %.6lf",f[((1<<n)-1)^(1<<i)]);
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值