9.10NOIP模拟赛

1.公约数
(gcd.cpp\c\pas)
【问题描述】
给定一个正整数,在[1,n]的范围内,求出有多少个无序数对(a,b)满足gcd(a,b)=a xor b。

【输入格式】
输入共一行,一个正整数n。
【输出格式】
输出共一行,一个正整数表示答案。
【输入输出样例】
gcd.in
3
gcd.out
1
解释:只有(2,3)满足要求
【数据范围】
对于30%的数据满足n<=1000
对于60%的数据满足n<=10^5
对于100%的数据满足n<=10^7

枚举c,再枚举a=i*c,显然gcd(a,c)=c,
如果a-c==a xor c,则ans++。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int n,ans=0;
int main() {
    freopen("gcd.in","r",stdin);
    freopen("gcd.out","w",stdout);
    scanf("%d",&n);
    for (register int i=1;i<=n;++i)
        for (int j=(i<<1);j<=n;j+=i)
            ans+=((j^i)==j-i);
    printf("%d\n",ans);
    return 0;
}

2.树上路径
(path.cpp\c\pas)
【问题描述】
给出一棵树,求出最小的k,使得,且在树中存在路径p,使得k>=S且k<=E。(k为路径p上的边的权值和)
【输入格式】
第一行给出N,S,E。N代表树的点数,S,E如题目描述。
下面N-1行给出这棵树的相邻两个节点的边及其权值W。
【输出格式】
输出共一行一个整数,表示答案。若无解输出-1。
【输入输出样例】
path.in
5 10 40
2 4 80
2 3 57
1 2 16
2 5 49
path.out
16
【样例解释】
1到2的路径即为答案。
【数据范围】
对于20%的数据满足n<=300
对于50%的数据满足n<=3000
对于60%的数据满足n<=10^5
对于以上数据,满足|E-S|<=50
对于100%的数据满足n<=10^5,|E-S|<=10^6
对于所有数据满足1<=Wi<=1000,|E|,|S|<=10^9

裸的点分治(虽然本蒟蒻并没有看出来),对于每个分治重心用双指针扫一遍即可,复杂度O(nlog(n))
注意:属于当前同一个子树的答案不能计算,因为后面会更新到,所以要记录一个belong[ ]数组。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5+2,INF=0x7fffffff;
int n,S,E,ans=INF,root=0,sum,bel[MAXN],dis[MAXN],siz[MAXN],f[MAXN],vis[MAXN],belong[MAXN];
int head[MAXN],edge=0;
struct EDGE {
    int v,nxt,w;
}e[MAXN<<1];
struct NODE {
    int len,bel,ne;
    friend bool operator <(const NODE &p,const NODE &q) {
        return p.len<q.len;
    }
}a[MAXN];int cnt=0;
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'&&c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
inline void adde(int u,int v,int w) {
    e[edge].nxt=head[u],e[edge].v=v,e[edge].w=w,head[u]=edge++;
    e[edge].nxt=head[v],e[edge].v=u,e[edge].w=w,head[v]=edge++;
}
void getroot(int p,int fa) {
    siz[p]=1,f[p]=0;
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (!vis[v]&&(v^fa)) {
            getroot(v,p);
            siz[p]+=siz[v];
            f[p]=max(f[p],siz[v]);
        }
    }
    f[p]=max(f[p],sum-siz[p]);
    if (f[p]<f[root]) root=p;
}
void getdis(int p,int fa) {
    a[++cnt].len=dis[p],a[cnt].bel=belong[p];
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (!vis[v]&&(v^fa)) {
            dis[v]=dis[p]+e[i].w;
            belong[v]=fa?belong[p]:v;
            getdis(v,p);
        }
    }
}
void cal(int x) {
    cnt=0;
    dis[x]=0;
    belong[x]=x;
    getdis(x,0);
    sort(a+1,a+cnt+1);
    a[cnt].ne=cnt+1;
    for (int i=cnt-1;i;--i)
        a[i].ne=(a[i].bel^a[i+1].bel)?i+1:a[i+1].ne;
    for (int l=1,r=cnt;l<r;++l) {
        while (a[r-1].len+a[l].len>=S) --r;
        if (a[l].bel==a[r].bel) r=a[r].ne;
        int temp=a[l].len+a[r].len;
        if (r<=cnt&&temp>=S&&temp<=E)
            ans=min(ans,temp);
    }
}
void work(int pos) {
    cal(pos);
    vis[pos]=1;
    for (int i=head[pos];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (!vis[v]) {
            sum=siz[pos];
            root=0;
            getroot(v,pos);
            work(root);
        }
    }
}
int main() {
    freopen("path.in","r",stdin);
    freopen("path.out","w",stdout);
    memset(siz,0,sizeof(siz));
    memset(head,-1,sizeof(head));
    n=read(),S=read(),E=read();
    for (register int i=1;i<n;++i) {
        int u=read(),v=read(),w=read();
        adde(u,v,w);
    }
    sum=n;
    f[0]=INF;
    getroot(1,0);
    work(root);
    printf("%d\n",ans^INF?ans:-1);
    return 0;
}

3.飞扬的小鸟
(bird.cpp\c\pas)
【问题描述】
Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
现在小鸟们遇到了一个难题,他们遇到了一堵巨大的墙,墙上仅有m个洞供他们通过,由于小鸟们的体型不同且墙上洞的形状也不同,所以每种体型的鸟通过每个洞的时间都不同,鸟的体型共有n种,第i种体型的鸟通过第j个洞需要的时间记为T(i,j),且一个洞必须前一只鸟通过之后后一只鸟才能开始通过。
从时刻0开始,鸟开始通过,而每一只鸟的等待时间为从时刻0到自己已经通过洞的时间。现在知道了第i种体型的鸟有pi只,请求出使所有鸟都通过墙的最少的等待时间之和。
【输入格式】
第1行包含两个正整数n和m,表示鸟的体型的种数和墙洞的数量。
第2行包含n个正整数,其中第i个数为pi,表示点第i种体型的鸟的只数。
接下来有n行,每行包含m个非负整数,这n行中的第i行的第j个数为t(i,j),表示第i种体型的鸟通过第j个墙洞所需的时间。输入文件中每行相邻的两个数之间均由一个空格隔开,行末均没有多余空格。
【输出格式】
输出仅一行包含一个整数,为总等待时间的最小值。
【输入输出样例】
bird.in
3 2
3 1 1
5 7
3 6
8 9
bird.out
47
【数据范围】
每组数据的n、m和p值如下:
序号 n m p
1 5 5 10
2 40 1 400
3 40 2 300
4 40 40 40
5 5 40 100
6 10 50 200
7 20 60 400
8 40 80 600
9 40 100 800
10 40 100 800

对于100%的数据,n<=40,m<=100,p<=800,t(i,j)<=1000(p为鸟的总只数)

本人的代码抄的是别人的动态加边费用流
细节注释在代码中,解题思路就懒得写了。。。想看的戳上面的博客。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=100002,INF=0x3f3f3f3f;
int n,m;
int num[42],tag[42][802];
int head[maxn],edge,source,sink,sum=0;
struct EDGE {
    int u,v,nxt,r,c;
}e[maxn<<2];
int dis[maxn],pre[maxn];
bool inq[maxn];
inline void adde(int u,int v,int r,int c) {
    e[edge].u=u,e[edge].v=v,e[edge].nxt=head[u],e[edge].r=r,e[edge].c=c,head[u]=edge++;
    e[edge].u=v,e[edge].v=u,e[edge].nxt=head[v],e[edge].r=0,e[edge].c=-c,head[v]=edge++;
}
inline bool spfa() {
    queue<int> q;
    memset(inq,false,sizeof(inq));
    memset(dis,INF,sizeof(dis));
    dis[source]=0,q.push(source),inq[source]=true,pre[source]=-1;
    while (!q.empty()) {
        int p=q.front();
        q.pop(),inq[p]=false;
        for (int i=head[p];~i;i=e[i].nxt) {
            int v=e[i].v;
            if (e[i].r>0&&dis[v]>dis[p]+e[i].c) {
                pre[v]=i,
                dis[v]=dis[p]+e[i].c;
                if (!inq[v]) {
                    inq[v]=true;
                    q.push(v);
                }
            }
        }
    }
    return dis[sink]^INF;
}
inline int MCMF() {
    int ans=0;
    while (spfa()) {
        int tmp=INF;
        for (int i=pre[sink];~i;i=pre[e[i].u]) tmp=min(tmp,e[i].r);
        ans+=tmp*dis[sink];
        for (int i=pre[sink];~i;i=pre[e[i].u]) e[i].r-=tmp,e[i^1].r+=tmp;
        int pt=e[pre[sink]^1].v;//反向边
        int x=(pt-n-1)%sum+1,y=(pt-n-1)/sum+1;//-1:防止模出0来/使i*sum和i*sum-1除出来一样
        if (x==sum) break;
        for (int i=1;i<=n;++i)
            adde(i,n+(y-1)*sum+x+1,1,(x+1)*tag[i][y]);
    }
    return ans;
}
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
int main() {
    freopen("bird.in","r",stdin);
    freopen("bird.out","w",stdout);
    memset(head,-1,sizeof(head));
    n=read(),m=read();
    for (int i=1;i<=n;++i) num[i]=read(),sum+=num[i];
    source=0,sink=n+sum*m+1;
    for (int i=1;i<=n;++i) {
        adde(0,i,num[i],0);
        for (int j=1;j<=m;++j)
            adde(i,n+(j-1)*sum+1,1,tag[i][j]=read());
            //+1:先对当前洞的最后一个的点加边,如果满流就在MCMF()中继续加边
    }
    for (int i=1;i<=sum;++i)
        for (int j=1;j<=m;++j)
            adde(n+(j-1)*sum+i,sink,1,0);
    printf("%d\n",MCMF());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值