NOIP2017清北综合强化试题

Day1

Problem 1:
这里写图片描述
这里写图片描述
a[]数组从右向左记录所有友谊值对应二进制位上1的数量之和,考虑每存在一个0,则对答案有一次贡献。设从左向右第i位上,n为1的数量,k为0的数量,ans=(1<< i)* n* k,倒叙依次处理即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
long long n,x,k,j,ans;
long long a[100010];//从左向右储存对应二进制位上1的数量 
int main()
{
    scanf("%I64d",&n);
    for (int i=1;i<=n;++i)
    {
        scanf("%I64d",&x);
        j=0;
        while(x>0)
        {
            j++;
            a[j]=a[j]+x%2;
            x=x/2;
        }
        if (j>k) k=j;//更新二进制下最长长度 
    }
    for (int i=k;i>=1;--i)
    ans=ans*2+a[i]*(n-a[i]);
    printf("%I64d\n",ans);
}

Problem 2:
这里写图片描述
这里写图片描述
每增加一个斜率不同且零点不同的函数,则增加一个非180度角,统计零点个数即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int nn=0,b[100001],k[100001];
long double cc[100001];
int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;++i)
    {
        int kk,bb;
        scanf("%d%d",&kk,&bb);
        if (kk)
        {
            nn++;
            b[nn]=bb;
            k[nn]=kk;
            cc[nn]=-(long double)b[nn]/(long double)k[nn];//零点
        }
    }
    int ans=nn;
    n=nn;
    if(n==0)
    {
        printf("0\n");
        return 0;
    }
    sort(cc+1,cc+n+1);
    for(int i=2;i<=n;i++)
    if(fabs(cc[i]-cc[i-1])<1e-18)//去重,忽略精度误差
    ans--;
    printf("%d\n",ans);
}

Problem 3:
这里写图片描述
这里写图片描述
这里写图片描述
DP…下一个

Day2

Problem 1:
这里写图片描述
组合数公式:
这里写图片描述
计算n的阶乘,费马小定理求解m,n-m阶乘的逆元,中间过程取模最后相乘即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 211111;
const int mod = 1000000007;
int n,m;
int fact[N],inverse[N];
int powmod(int x, int y)
{
    if(!y)
    return 1;
    int ret=powmod(x,y>>1);
    if (y&1)
    return 1LL * ret * ret % mod * x % mod;
    else return 1LL * ret * ret % mod;
}
int main() 
{
    scanf("%d%d",&n,&m);
    fact[0]=1;
    inverse[0]=1;
    for (int i=1;i<=n;i++) 
    {
        fact[i]=1LL*fact[i-1]*i%mod;
        inverse[i]=powmod(fact[i],mod-2);
    }
    int ans=1LL*fact[n]*inverse[n-m]%mod*inverse[m]%mod;
    printf("%d\n",ans);
    return 0;
}

Problem 2:
这里写图片描述
这里写图片描述
DP…下一个
Problem 3:
这里写图片描述
这里写图片描述
DP…下一个

Day3

Problem 1:
这里写图片描述
这里写图片描述
DP…
Problem 2:
这里写图片描述
这里写图片描述
DP…
Problem 3:
这里写图片描述
这里写图片描述
贪心法求解

Day4

Problem 1:
这里写图片描述
这里写图片描述
这里写图片描述
Problem 2:
这里写图片描述
这里写图片描述
这里写图片描述
Problem 3:
这里写图片描述
这里写图片描述
这里写图片描述

Day5

Problem 1:
这里写图片描述
这里写图片描述
设一次切割后剩余两块蛋糕大小为p,q且q>p
切割次数/大小/结束条件
1:p,q / p=q
2:q-p,p / 2p=q
3:p-(q-p),q-p / 3p=2q
4:(q-p)-p+(q-p),p-(q-p) / 5p=3q
观察得,若要使切割次数最大,A吃的的最多则需安装Fibonacci数列的比例切割,
则尝试用每一种使第一次切割符合Fib数列的方案切下去,取蛋糕数量最多的方案即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
long long n,m;
long long fib[100];
long long calc(long long a,long long b)//确定了第一次的切割方式,切到相等为止,记录答案
{
    if(a==b)
    return a;
    if(a<b)
    swap(a,b);
    return calc(b,a-b);
}
long long check(long long x,long long y)
{
    fib[0]=1;
    fib[1]=1;
    for(int i=2;i<75;i++)//预处理Fib数列
    fib[i]=fib[i-1]+fib[i-2];
    long long res=0;
    for(int i=1;i<70;i++)
    {
        if (x*fib[i]%fib[i+1]==0)
        {
            long long z=x*fib[i]/fib[i+1];
            res=max(res,(x-calc(x,z))*y);//*y计算面积,就第一次切割而言,满足Fib数列的情况有可能有很多种,取max
        }
    }
    return res;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    printf("%lld\n",max(check(n,m),check(m,n)));//分别检索横切和竖切的情况
}

Problem 2:
这里写图片描述
这里写图片描述
(0,0),(0,1),(1,0),(1,1)为黑色格子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n;
long long X[120000],Y[120000],ansx,ansy;
void solve(long long x,long long y,int c)
{
    long long p=(x>>1)*(y>>1)+((x+1)>>1)*((y+1)>>1);
    long long q=x*y-p;
    ansx+=p*c;
    ansy+=q*c;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%lld%lld",&X[i],&Y[i]);
    X[n+1]=X[1];Y[n+1]=Y[1];
    for(int i=1;i<=n;i++)
    {
        if(Y[i]==Y[i+1])
        {
            solve(X[i],Y[i],1);
            solve(X[i+1],Y[i+1],-1);
        }
    }
    printf("%lld %lld\n",ansx,ansy);
}

Problem 3:
这里写图片描述
这里写图片描述
对于100%的数据,n<=10。
计算几何

Day6

Problem 1:
这里写图片描述
这里写图片描述
输出最小的一个c
http://blog.csdn.net/qq_36312502/article/details/78166665 cgold简洁明了的解释:
对于某个数
t | 12;
显然 : t=2^x * 3^y;
其中 0<=x<=2, 0<=y<=1;
所以,12的因子个数为(2+1)*(1+1);

具体程序中表现为(i+1)为选择当前质数的个数(次数)。
sum表示当前选择的质数的乘积,nowd表示sum的质因子数。
对于2^3*3^2和2^2*3^3,两者因子数一致但前者小于后者,为了使答案尽量小则可在因子数尽量多的情况下,使较小因子的次数尽量大
若选到了超过第11个质数,则必定可以将此质数换为2的次幂加入答案,无继续搜索必要。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
long long n,maxd,ans;
int prime[]={0,2,3,5,7,11,13,17,19,23,29,31,37,41};//预先写出一些质数使用
void dfs(int x,long long sum,long long nowd,long long Limit)
{
    if (nowd>maxd||(nowd==maxd&&sum<ans))
    maxd=nowd,ans=sum;
    if (x>11)
    return;
    for (int i=1;sum*prime[x]<=n&&i<=Limit;i++)
    dfs(x+1,sum*=prime[x],nowd*(i+1),i); 
}
int main()
{
    scanf("%I64",&n);
    ans=1;
    if(n>1)
    dfs(1,1,1,1e9);
    printf("%I64\n",ans);
    return 0;  
}

Problem 2:
这里写图片描述
这里写图片描述
对子树Hash,STL存储即可

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int N = 120000;
vector<int>E[N];//邻接表村图
map<vector<int>,int>id;//子树到构造标号的映射
int ans[N],p[N],vis[N];
int n,cnt;
int work(int x)
{
    vector<int>u;
    for(int i=0;i<E[x].size();i++)
    {
        int y=E[x][i];
        u.push_back(work(y));
    }
    sort(u.begin(),u.end());
    if(!id[u])
    id[u]=++cnt;
    ans[x]=id[u];
    return ans[x];
}
int main()
{
    scanf("%d",&n);
    for(int i = 2; i <= n; i++)
    {
        scanf("%d",&p[i]);
        E[p[i]].push_back(i);
    }
    work(1);
    cnt=0;
    for(int i=1;i<=n;i++)//转换为字典序最小的标号
    {
        if(!vis[ans[i]])
        vis[ans[i]]=++cnt;
        ans[i]=vis[ans[i]];
    }
    for(int i=1;i<=n;i++)
    printf("%d ",ans[i]);
    puts("");
}

Problem 3:
这里写图片描述
这里写图片描述
输出只要为满足条件的答案即可
差分约束系统,可转化为图论的单源最短/长路径问题求解。
这里为最长路,保证满足路径上的所有条件。
若Rxy=1,由x向y+n建一条权值为1的单向边
若Rxy=0,由y+n向x建一条权值为1的单向边
从每个入度为0的点(或建立一个额外的点向所有入度为0的点建一条权值为0的单向边)topsort,并求出到每一点的最短路,1到n号点表示f[]内元素,n+1到2n表示g[]内元素,如果存在环则无解。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int INF = 100000009;
int n,tot;
int first[500010],nxt[500010],dis[100010],ent[500010];
bool flag;
bool vis[500010];
char ch[100010];
struct edge
{
    int u,v,w;
}l[500010];
queue<int>q;
void build(int f,int t)
{
    l[++tot]=(edge){f,t,1};
    nxt[tot]=first[f];
    first[f]=tot;
}
void topsort()
{
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        for(int i=first[k];i!=-1;i=nxt[i])
        {
            int x=l[i].v;
            if(dis[x]<dis[k]+l[i].w)
            dis[x]=dis[k]+l[i].w;
            ent[x]--;
            vis[x]=1;
            if(!ent[x])
            q.push(x);
        }
    }
}
int main()
{
    memset(first,-1,sizeof(first));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",&ch);
        for(int j=0;j<n;j++)
        {
            if(ch[j]=='1')
            build(i,j+1+n),ent[j+1+n]++;
            else build(j+1+n,i),ent[i]++;
        }
    }
    for(int i=1;i<=2*n;i++)
    {
        if(!ent[i])
        q.push(i),vis[i]=1;
    }
    topsort();
    for(int i=1;i<=2*n;i++)//如果存在没有被访问过的点,则说明存在环
    {
        if(!vis[i])
        {
            printf("NO\n");
            return 0;
        }
    }
    printf("YES\n");
    for(int i=1;i<=n;i++)
    printf("%d ",dis[i]);
    puts("");
    for(int i=n+1;i<=2*n;i++)
    printf("%d ",dis[i]);
    return 0;
}

Day7

Problem 1:
这里写图片描述
这里写图片描述
这里写图片描述
对于节点值u< x< v,考虑只要两种情况:
这里写图片描述
双向链表,或者可以写权值线段树

#include <iostream>
#define MAXN 300005
int n;
int a[MAXN];
int prev[MAXN],next[MAXN];
int u[MAXN],v[MAXN];
int deep[MAXN];
int main(void)
{

    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d",a+i);

    for (int i=1;i<=n;i++)
    {
        prev[i]=i-1;
        next[i]=i+1;
    }
    int x;
    for (int i=n;i>i;i--)
    {
        x=a[i];
        u[x]=prev[x];
        v[x]=next[x];
        next[prev[x]]=next[x];
        prev[next[x]]=prev[x];
    }
    long long sum=0;
    for (int i=1;i<=n;i++)
    {
        x = a[i];        
        if((u[x]>=1)&&(u[x]<=n))
            if(deep[x]<deep[u[x]]+1)
                deep[x]=deep[u[x]]+1;
        if((v[x]>=1)&&(v[x]<=n))
            if(deep[x]<deep[v[x]]+1)
                deep[x]=deep[v[x]]+1;

        sum+=deep[a[i]];
        printf("%lld\n",sum);
    }
    return 0;
}

Problem 2:
这里写图片描述
这里写图片描述
Problem 3:
这里写图片描述
这里写图片描述
逐步补全吧…

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值