集训队日常训练20180518-DIV1

A.3583

n根木棍是否能分成相等两堆。

背包dp,首先求和sum,如果为偶数就说明不行,否则考虑做一个sum/2大小的背包。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     int n,w[105];
 7     while(scanf("%d",&n)!=EOF)
 8     {
 9         int sum=0;
10         for(int i=1;i<=n;i++)scanf("%d",&w[i]),sum+=w[i];
11         bool dp[10005];
12         memset(dp,0,sizeof dp);
13         dp[0]=1;
14         for(int i=1;i<=n;i++)
15             for(int j=sum/2;j>=w[i];j--)
16                 dp[j]=dp[j]|dp[j-w[i]];
17         printf("%s
",dp[sum/2]&&sum%2==0?"Yes":"No");
18     }
19     return 0;
20 }
A.cpp

B.5146

单身狗在[l,r]之间且二进制中1的数量最多的数,若有多个输出最小的。

贪心,可以发现想要二进制最多,那么一定从最低位开始。

那么想要在[l,r]之间,先把l变成二进制,贪心即可(想想为什么)。

#include<bits/stdc++.h>
using namespace std;

#define ll __int64
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int a[105]={0},p=0;
        ll l,r,ans;
        scanf("%I64d%I64d",&l,&r);
        ans=l;
        while(l)
        {
            a[p++]=l&1;
            l>>=1;
        }
        for(int i=0;i<=60;i++)
        {
            if(a[i]==0&&ans+(1LL<<i)<=r)
            {
                ans+=1LL<<i;
                a[i]=1;
            }
        }
        printf("%I64d
",ans);
    }
    return 0;
}
B.cpp

C.3153

n个点m条无向边,问必须经过S<=10个点再回到起点0的最短路。

dijstra+状压dp,考虑对每个S跑一边dijstra,求出S到每个点的最短路。

dp[i][j]代表状态为i,现在在j点的最短路。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=100005;
vector<pair<int,int> > G[N];
ll d[10][N],dp[1<<10][10];
int a[10],T,n,m;
void dij(int p,int u)
{
    for(int i=0;i<n;i++)
        d[p][i]=1LL<<60;
    d[p][u]=0;
    queue<int> qu;
    qu.push(u);
    while(!qu.empty())
    {
        int x=qu.front();
        qu.pop();
        for(int i=0;i<G[x].size();i++)
        {
            int v=G[x][i].first,c=G[x][i].second;
            if(d[p][v]>d[p][x]+c) d[p][v]=d[p][x]+c,qu.push(v);
        }
    }
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) G[i].clear();
        for(int i=0,u,v,c;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&c);
            G[u].push_back({v,c});
            G[v].push_back({u,c});
        }
        scanf("%d",&m);
        int S=1<<m;
        for(int i=0;i<m;i++) scanf("%d",&a[i]),dij(i,a[i]);
        for(int i=0;i<S;i++)
            for(int j=0;j<m;j++)
                dp[i][j]=1LL<<60;
        for(int i=0;i<m;i++)
            dp[1<<i][i]=d[i][0];
        for(int s=1;s<S;s++)
        {
            for(int i=0;i<m;i++)
                if((1<<i)&s)
                {
                    for(int j=0;j<m;j++)
                        if(((1<<j)&s)==0)
                            dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+d[i][a[j]]);
                }
        }
        ll ans=1LL<<60;
        for(int i=0;i<m;i++)
            ans=min(ans,dp[S-1][i]+d[i][0]);
        printf("%I64d
",ans);
    }
    return 0;
}
C.cpp

D.3176

n<=30个值,每个值(0,2^16),求最小需要几个数的异或后值为0,每个数只能取1次。

背包dp,考虑dp[i]代表异或值为i的最少个数。

#include<bits/stdc++.h>
using namespace std;

int main()
{
    int n,x,dp[1<<16];
    memset(dp,0x3f3f3f3f,sizeof dp);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        for(int j=(1<<16)-1;j>=0;j--)
            dp[j^x]=min(dp[j^x],dp[j]+1);
        dp[x]=1;
    }
    if(dp[0]==0x3f3f3f3f)printf("-1");
    else printf("%d",dp[0]);
}
D.cpp

E.3166

给一堆等级,和每个等级的区间,然后n个初始得分,每小时加1分,每个查询输出D小时后达到等级L的人数。

二分,从小到大排序,那么就是有多少个数ai+D在等级L的区间内,就相当于区间-D,然后二分左右段点。

#include<bits/stdc++.h>
using namespace std;

const int N=1e5+5;
int a[N],n,q,d,l,L,R;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&d,&l);
        if(l==1)L=0,R=100;
        if(l==2)L=101,R=500;
        if(l==3)L=501,R=2000;
        if(l==4)L=2001,R=10000;
        if(l==5)L=10001,R=50000;
        if(l==6)L=50001,R=200000;
        if(l==7)L=200001,R=2e9+7;
        int x1=lower_bound(a+1,a+1+n,L-d)-a;
        int x2=upper_bound(a+1,a+1+n,R-d)-a;
        printf("%d
",x2-x1);
    }
    return 0;
}
E.cpp

F.2704

n个点m条无向边边权为1,从1出发问距离最远的点,如有多个输出编号最小的点,再输出距离,再输出有几个点。

dijstra模板题。

#include<bits/stdc++.h>
using namespace std;

const int N=2e4+5;
vector<int>G[N];
int n,m,u,v;
void bfs()
{
    int d[N];
    memset(d,0x3f3f3f3f,sizeof d);
    queue<int>q;
    d[1]=0;
    q.push(1);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            if(d[v]>d[u]+1)
            {
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    int maxx=-1,posi=0,ans=0;
    for(int i=1;i<=n;i++)
        if(d[i]>maxx)maxx=d[i],posi=i;
    for(int i=1;i<=n;i++)
        if(d[i]==maxx)ans++;
    cout<<posi<<" "<<maxx<<" "<<ans;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    bfs();
    return 0;
}
F.cpp

G.3168

n瓶奶生产商和生产日期保证2008年,m个生产商对应奶的保质期,给你一个开始的日期,每天最多喝一瓶,问最少几瓶会过期。

哈希+贪心,把每个日期转化成天,优先处理过期时间最早的。

#include<bits/stdc++.h>
using namespace std;
int a[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
vector<int>v[100];
int b[600];
int main()
{
    std::ios::sync_with_stdio(false); 
    map<string,int>m;
    int id=1,n,x,y;
    char ch;
    string s;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>s;
          if(m[s]==0)
            m[s]=id++;
          cin>>x>>ch>>y;
          int day=0;
          for(int j=1;j<x;j++)
            day+=a[j];
        day+=y;
        v[m[s]].push_back(day);
    }
    int t;
    cin>>t;
    for(int i=1;i<=t;i++)
    {
        cin>>s>>x;
        for(int j=0;j<v[m[s]].size();j++)
            b[v[m[s]][j]+x]++;
    }
    cin>>x>>ch>>y;
    int day=0;
    for(int j=1;j<x;j++)
        day+=a[j];
    day+=y;
    int sum=0;
    for(int i=1;i<=day;i++)
        sum=sum+b[i];
    int p=day;
    for(int i=day+1;i<600;i++)
    {
        while(b[i]>0)
        {
            if(p<i)
                p++,b[i]--;
            else
            {
                sum+=b[i];
                break;
            }
        }
    }
    cout<<sum<<endl;
    return 0;
}
G.cpp

H.3213

n<=8个题目选择k个难度总和<=p求方案数。

n很小,直接暴力dfs。

#include <bits/stdc++.h>
using namespace std;
int v[10],n,k,p,ans;
void DFS(int pos,int s,int cnt)
{
    if(s <= p && cnt == k) {ans++;return;}
    if(cnt > k || s > p) return;
    for(int i = pos; i < n; i++)
    {
        DFS(i + 1,s + v[i],cnt + 1);
    }
}
int main()
{
    int t; scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&k,&p);
        for(int i = 0; i < n; i++) scanf("%d",&v[i]);
        ans = 0;
        DFS(0,0,0);
        printf("%d
",ans);
    }
    return 0;
}
H.cpp

I.5069

给n*m的图,求一个四周全是‘.’的最大矩形。

思维,a为列前缀和,b为行前缀和,(i,j)---(i,k)那么第i行的a[i][k]-a[i][j]==k-j那么第i行j->k都是1,x=min(b[i][j],b[i][k])代表j向上或者k向上连续1的数量,然后只需要判断a[i-x+1][k]-a[i-x+1][j]==k-j,这就说明第i-x+1行j­->k都是1,复杂度O(n^3)。

#include<bits/stdc++.h>
using namespace std;

int n,m;
char G[205][205];
int a[205][205],b[205][205];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",G[i]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(G[i][j]=='.')
                a[i][j]=a[i][j-1]+1,
                b[i][j]=b[i-1][j]+1;
            else
                a[i][j]=0,
                b[i][j]=0;
    //(i,j)     (i,k)
    int maxx=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            for(int k=j;k<=m;k++)
            {
                if(a[i][k]-a[i][j]!=k-j)break;
                if(b[i][j]&&b[i][k])
                {
                    int x=min(b[i][j],b[i][k]);
                    while(a[i-x+1][k]-a[i-x+1][j]!=k-j)x--;
                    maxx=max(maxx,x*(k-j+1));
                }
            }
        }
    printf("%d",maxx);
    return 0;
}
I.cpp

J.4482

n个数的积为A,m个数的积为B,求A和B的gcd。

zdragon的O((n+m)*sqrt(1e9))

求A的所有素因子,求B的所有素因子,枚举所有素因子,答案就是素因子^(该素因子A有几个,该素因子B有几个)的积。

#include <bits/stdc++.h>
using namespace std;
const int N=1005;
const int MD=1000000000;
int a[N<<2],b[N<<2],cnt;
bool f;
map<int,int> ma,mma;
long long quick_pow(long long x,int y)
{
    long long ans=1;
    while(y)
    {
        if(y&1)
        {
            ans*=x;
            if(ans>MD||f) ans%=MD,f=true;
        }
        x*=x;
        if(x>MD||f) x%=MD,f=true;
        y>>=1;
    }
    return ans;
}
int main()
{
    int n,m;
    scanf("%d",&n);
    for(int i=0,x;i<n;i++)
    {
        scanf("%d",&x);
        for(int j=2;1LL*j*j<=x;j++)
        {
            if(x%j==0)
            {
                if(!ma[j]) ma[j]=(++cnt),mma[cnt]=j;
                while(x%j==0) a[ma[j]]++,x/=j;
            }
        }
        if(x>1)
        {
            if(!ma[x]) ma[x]=(++cnt),mma[cnt]=x;
            a[ma[x]]++;
        }
    }
    scanf("%d",&m);
    for(int i=0,x;i<m;i++)
    {
        scanf("%d",&x);
        for(int j=2;1LL*j*j<=x;j++)
        {
            if(x%j==0)
            {
                if(!ma[j]) ma[j]=(++cnt),mma[cnt]=j;
                while(x%j==0) b[ma[j]]++,x/=j;
            }
        }
        if(x>1)
        {
            if(!ma[x]) ma[x]=(++cnt),mma[cnt]=x;
            b[ma[x]]++;
        }
    }
    long long ans=1;
    for(int i=1;i<=cnt;i++)
    {
        ans=ans*quick_pow(1LL*mma[i],min(a[i],b[i]));
        if(ans>MD||f) ans%=MD,f=true;
    }
    if(f) printf("%09I64d
",ans);
    else printf("%I64d
",ans);
    return 0;
}
J.cpp O((n+m)*sqrt(1e9))

wff的O(n*m)

暴力gcd。

#include <bits/stdc++.h>
using namespace std;
const int inf =0x3f3f3f3f;
#define ll long long

int a[1005],b[1005];
int main()
{
    ll ans1=1,ans2=0;
    int n,m;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d",&b[i]);
    }
    int flag=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<m;j++)
        {
            int t=__gcd(a[i],b[j]);
            ans1=1ll *ans1 * t ;
            if(!flag&&ans1>= 1000000000) flag=1;
            ans1%=1000000000;
            a[i]/=t;
            b[j]/=t;
        }
    }
    if(flag) printf("%09I64d",ans1% 1000000000);
    else printf("%I64d",ans1% 1000000000);
    return 0;
}
J.cpp O(n*m)

K.4426

n个人初始分ai,第一名+n分,第二名+n-1分,以此类推,如果一个人i是冠军那么他的得分大于等于其他所有人的得分,问有几个人可能是冠军。

贪心,首先按ai从小到大排序,得分最高的加1分,第二高加2分,以此类推,maxx=ai+n-i+1的最大值,如果一个人i是冠军,那么ai+n大于等于maxx。

#include<bits/stdc++.h>
using namespace std;

const int N=3e5+5;
int a[N],n,ans,sum;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=n;i>=1;i--)ans=max(ans,a[i]+n-i+1);
    for(int i=1;i<=n;i++)if(a[i]+n>=ans)sum++;
    printf("%d",sum);
    return 0;
}
K.cpp

L.3457

给一个整数,输出重排列后的最小值。

贪心,首先第一个数不能为0,那么先取个不是0的最小放在第一个。然后按小到大输出。

#include<stdio.h>
#include<string.h>
int main()
{
    int i,j;
    char a[30];
    while(scanf("%s",&a)!=EOF)
    {
        int b[10]={0},min=10;
        for(i=0;i<strlen(a);i++)
        {
            b[a[i]-'0']++;
            if(a[i]-'0'<min&&a[i]-'0'!=0)
            min=a[i]-'0';
        }
        printf("%d",min);
        b[min]--;
        for(i=0;i<10;i++)
        {
            for(j=0;j<b[i];j++)
            printf("%d",i);
        }
        printf("
");
    }
    return 0;
}
L.cpp

M.2609

n个点m条无向边最多k次操作使得边花费为0,求1到n的最短路。

堆优化的dijstra+dp,分成k层,dp[k][i]表示已经让k条边花费变成0到i的最短路。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e4+5;
const int maxm=5e4+5;
const int maxk=20+5;

inline int read() {
   int x=0,f=1;char c=getchar();
   for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1;
   for(;'0'<=c&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
   return x*f;
}

vector< pair<int,int> >G[maxn];
int d[maxk][maxn],vis[maxk][maxn];
int n,m,k;
struct edge
{
    int v,k,w;
    bool operator<(const edge& D)const{
        return w>D.w; 
    }
};
int dij()
{
    for(int i=0;i<=k;i++)for(int j=0;j<=n;j++)d[i][j]=0x3f3f3f3f,vis[i][j]=0;
    priority_queue<edge>q;
    q.push({1,0,0});
    d[0][1]=0;
    while(!q.empty())
    {
        edge u=q.top();q.pop();
        //printf("%d %d
",u.v,u.w);
        if(u.v==n)return u.w;
        if(vis[u.k][u.v])continue;
        vis[u.k][u.v]=1;
        for(auto x:G[u.v])
        {
            int v=x.first;
            int w=x.second;
            if(d[u.k][v]>u.w+w)
                q.push({v,u.k,d[u.k][v]=u.w+w});
            if(u.k<k&&d[u.k+1][v]>u.w)
                q.push({v,u.k+1,d[u.k+1][v]=u.w});
        }
    }
    return 0x3f3f3f3f;
}
int main()
{
    n=read(),m=read(),k=read();
    for(int i=1,u,v,w;i<=m;i++)
    {
        u=read(),v=read(),w=read();
        G[u].push_back({v,w});
        G[v].push_back({u,w});
    }
    printf("%d
",dij());
    return 0;
}
M.cpp
原文地址:https://www.cnblogs.com/taozi1115402474/p/10884255.html