10.5考试总结

B组:

命运

这里写图片描述
这里写图片描述

50分:最小生成树,每两个点连个边
60分(考试分数):当只有1维时,只需对所有点坐标从小到大排序,选相邻的两个点连边即可
100分:发现若答案存在某条边 U>V 则U和V按某一维排序一定是挨着的,则每次对于这五维排序,对挨着的两个点连边做最小生成树即可

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 100020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,k,top;
long long ans;
int f[N];
struct Point{
    int pos[7],bh;
}a[N];
struct Edge{
    int x,y,k;
    Edge(int _=0,int __=0,int ___=0):x(_),y(__),k(___){}
}q[1000050];
inline void add(int x,int y,int k){
    q[++top]=Edge(x,y,k);
}
inline int abs(int a){return a>0?a:-a;}
int find(int k){return f[k]==k?f[k]:f[k]=find(f[k]);}
inline void link(int x,int y){
    int fx=find(x),fy=find(y);
    f[fx]=fy;
}
inline bool cmp(Edge a,Edge b){return a.k<b.k;}
inline bool cmp1(Point a,Point b){return a.pos[1]<b.pos[1];}
inline bool cmp2(Point a,Point b){return a.pos[2]<b.pos[2];}
inline bool cmp3(Point a,Point b){return a.pos[3]<b.pos[3];}
inline bool cmp4(Point a,Point b){return a.pos[4]<b.pos[4];}
inline bool cmp5(Point a,Point b){return a.pos[5]<b.pos[5];}
inline void solve(){
    for(int i=1;i<=k;i++){
        if(i==1)sort(a+1,a+n+1,cmp1);
        if(i==2)sort(a+1,a+n+1,cmp2);
        if(i==3)sort(a+1,a+n+1,cmp3);
        if(i==4)sort(a+1,a+n+1,cmp4);
        if(i==5)sort(a+1,a+n+1,cmp5);//按照5维排序
        for(int j=2;j<=n;j++){//连边,注意编号可能被打乱,要记录一下
            add(a[j-1].bh,a[j].bh,abs(a[j].pos[i]-a[j-1].pos[i]));
        }
    }
    sort(q+1,q+top+1,cmp);
    for(int i=1;i<=n;i++) f[i]=i;//最小生成树
    for(int i=1,cnt=0;i<=top&&cnt<=n-1;i++){
        int fx=find(q[i].x),fy=find(q[i].y);
        if(fx==fy) continue;
        link(fx,fy);
        ans+=q[i].k;
        cnt++;
    }
    printf("%lld",ans);
}
int main(){
    n=read();k=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++)
            a[i].pos[j]=read(),a[i].bh=i;
    solve();
return 0;
}

幻想

这里写图片描述
这里写图片描述

spfa, disi,j 表示到点i用了j次更改权值的机会,转移很好想吧…

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<queue>
#define N 10020
#define M 2000200
#define int long long
#define INF 214748364700000000ll
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
queue<int>q;
int n,m,k,x,y,z,s,t,top;
int Dis[N],fir[N];
struct Edge{
    int nex,to,k;
    Edge(int _=0,int __=0,int ___=0):to(_),nex(__),k(___){}
}nex[M];
inline void add(int x,int y,int k){
    nex[++top]=Edge(y,fir[x],k);
    fir[x]=top;
}
bool b[N];
inline bool spfa1(){
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++) Dis[i]=INF,b[i]=false;
    Dis[s]=0;q.push(s);b[s]=true;
    while(!q.empty()){
        int x=q.front();q.pop();b[x]=false;
        for(int i=fir[x];i;i=nex[i].nex)
            if(Dis[nex[i].to]>Dis[x]+nex[i].k){
                Dis[nex[i].to]=Dis[x]+nex[i].k;
                if(!b[nex[i].to]){
                    b[nex[i].to]=true;
                    q.push(nex[i].to);
                }
            }
    }
    return Dis[t]==INF;
}
int dis[N][55],ans;
bool d[N][55];
inline void spfa(){
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++)
            dis[i][j]=INF;
        dis[i][0]=Dis[i];
        b[i]=false;
    }
    for(int j=1;j<=k;j++){//从小到大枚举j
        dis[s][j]=0;q.push(s);b[s]=true;
        while(!q.empty()){
            int x=q.front();q.pop();b[x]=false;
            for(int i=fir[x];i;i=nex[i].nex){
                    if(dis[nex[i].to][j]>dis[x][j]+nex[i].k){
                        dis[nex[i].to][j]=dis[x][j]+nex[i].k;
                        if(!b[nex[i].to]){
                            b[nex[i].to]=true;
                            q.push(nex[i].to);
                        }
                    }
                    if(dis[nex[i].to][j]>dis[x][j-1]+nex[i].k/2){
                        dis[nex[i].to][j]=dis[x][j-1]+nex[i].k/2;
                        if(!b[nex[i].to]){
                            b[nex[i].to]=true;
                            q.push(nex[i].to);
                        }
                }
            }
        }
    }
}
inline void solve(){
    spfa();
    ans=INF;
    for(int i=0;i<=k;i++)
        ans=min(ans,dis[t][i]);
    printf("%lld",ans);
}
main(){
    n=read();m=read();k=read();
    s=read();t=read();
    for(int i=1;i<=m;i++){
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    if(spfa1()){
        printf("baka");
        return 0;
    }//判断是否联通
    if(k==0){
        printf("%lld",Dis[t]);
        return 0;
    }//水分..
    solve();
return 0;
}

读书

这里写图片描述
这里写图片描述

Meet-in-Middle…
考虑m为0的情况,则只要让体力消耗小于k即可
在MiM后对左右两段消耗体力值从小到大排序,定义两个指针i,j分别指向s1的开头和s2的结尾
固定i,从右到左枚举j直到合法,此时的j即为可统计的答案,下次查找只需将i右移一位,接着左移j即可

如果m不为0,怎么办?

采用类似的思想,不同的是按照能造成的伤害排序,伤害是否合法按照刚才那样处理就可以,对于统计体力在给定范围内的,开个权值树状数组统计一下就好(见代码)

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 27
using namespace std;
inline int read(){
    int x=0,f=1; char c;
    do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,m,k;
long long ans=0;
int top1,top2;
int q[1000020];
inline void add(int x,int k){
    for(;x<=1000000;x=x+(x&-x))
        q[x]+=k;
}
inline int Sum(int x){
    int sum=0;
    for(;x;x=x-(x&-x))
        sum=sum+q[x];
    return sum;
}
struct Round{
    int p,def,atk,a,b,c;
}a[N];
struct Data{
    int res,hp;
    Data(int _=0,int __=0):res(_),hp(__){}
}s1[600500],s2[600500];//res:消耗多少体力 hp:造成多少伤害
void dfs(int x,int res,int hp){
    if(x==(n>>1)+1){
        s1[++top1]=Data(res,hp);
        return;
    }
    dfs(x+1,res+a[x].p,hp+a[x].a);
    dfs(x+1,res+a[x].p-a[x].def,hp+a[x].a-a[x].b);
    dfs(x+1,res+a[x].p+a[x].atk,hp+a[x].a+a[x].c);
    return;
}//Meet-in-Middle left
void dfs1(int x,int res,int hp){
    if(x==n+1){
        s2[++top2]=Data(res,hp);
        return;
    }
    dfs1(x+1,res+a[x].p,hp+a[x].a);
    dfs1(x+1,res+a[x].p-a[x].def,hp+a[x].a-a[x].b);
    dfs1(x+1,res+a[x].p+a[x].atk,hp+a[x].a+a[x].c);
    return;
}//Meet-in-Middle right
inline bool cmp(Data a,Data b){
    return a.hp>b.hp;
}
inline void solve(){
    dfs(1,0,0);
    dfs1(n/2+1,0,0);
    sort(s1+1,s1+top1+1,cmp);
    sort(s2+1,s2+top2+1,cmp);//这次按造成伤害值从小到大排序
    int j=top2;
    for(int i=1;i<=j;i++) add(s2[i].res,1);
    for(int i=1;i<=top1;i++) {
        while(s2[j].hp+s1[i].hp<m && j) {add(s2[j].res,-1);j--;}//让造成伤害值始终大于m
        if(k-s1[i].res<0) continue;
        ans=ans+Sum(k-s1[i].res);//统计此时满足体力限定的方案个数
    }
    printf("%lld",ans);
    return;
}
inline void init();
int main(){
    init();
    solve();
    return 0;
}
inline void init(){//读入...
    n=read();m=read();k=read();
    for(int i=1;i<=n;i++)a[i].p=read();
    for(int i=1;i<=n;i++)a[i].a=read();
    for(int i=1;i<=n;i++)a[i].def=read();
    for(int i=1;i<=n;i++)a[i].b=read();
    for(int i=1;i<=n;i++)a[i].atk=read();
    for(int i=1;i<=n;i++)a[i].c=read();
}

A组:

Return

题目描述
他曾经将此作为一种高贵的气质、只留下潇洒的背影,直到他品尝了孤独的滋味、听到黑暗的夜里一个人默默的心跳。他向那个背影追去,却发现又回到原点。
现实中他依然在奔跑,他越跑越快,知道回到最初的时间,于是他又重新开始。然而轮回中、他早已死去,只有在梦魇中他才有一丝气息。
奔跑中的每一秒他都有一个速度,在现实中当然是越来越快的,直到回到最初的速度;在梦里,他同样在奔跑,不过梦,终究只是现实的闪回罢了,逃脱不了无尽的命运。现在你知道他在梦里每一秒的速度,梦里的一秒、即为现实中的一秒。请你统计一下梦里有多少秒在现实中满足 (现实中的上一秒的速度+这一秒的速度)mod(231 -1)=现实中的下一秒的速度。
这是一个长长的梦,所以现实中的每一秒都会在梦里出现的,不必担心会有断裂的时空存在。
输入描述
多组数据。
每组数据第一行一个整数 n,表示梦的长度。
第二行有 n 个整数,第 i 个整数表示梦中第 i 秒他的速度,保证在[0,231 ).
输出描述
对于第 i 组数据输出一行 Case #i: answer;如果现实只有一秒,他以恒定的速度在宇宙中茕茕而行,就像生活那样单调,就像清水一样淡,那么输出-1.
输入样例
3
1 2 3
5
1 2 3 5 7
6
2 3 1 2 7 5
输出样例
Case #1: 1
Case #2: 2
Case #3: 3
数据范围
对于%100 的数据,保证数据组数≤10.
对于%10 的数据,有 n=1.
对于%30 的数据,有 n≤3
对于%60 的数据,有 n≤1000
对于%100 的数据,有 n≤10000

样例3的解释:

现实中的速度是…5 7 1 2 3 5 7 1 2 3 5 7 1 2…

所以在梦里第一秒的速度是2,那么它在现实中前一秒是1,后一秒是3,所以1+2=3,是符合条件的。

梦中第二秒是3,2+3=5,get!

梦中第三秒是1,7+1≠2.

第四秒是2,1+2=3.

第五秒是7,5+7≠1.

第六秒是5,3+5≠7

所以答案就是3啦~


语文题…读懂之后才发现特别水…
sort+unique就行了…
对了-1还要特判一下

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#define N 10050
#define int long long
#define MOD 2147483647
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,top,pre,ans,cnt,p,x;
int a[N],b[N];
main(){
    while(scanf("%lld",&n)!=EOF){
        ans=0ll;
        for(int i=1;i<=n;i++) b[i]=a[i]=read();
        sort(b+1,b+n+1);
        top=unique(b+1,b+n+1)-b-1;
        if(top==1){
            printf("Case #%lld: %d\n",++p,-1);
            continue;
        }//特判无解
        b[0]=b[top];b[top+1]=b[1];
        for(int i=1;i<=n;i++){
            x=lower_bound(b+1,b+top+1,a[i])-b;
            if((b[x-1]+a[i])%MOD==b[x+1]) ans++;
        }
        printf("Case #%lld: %lld\n",++p,ans);
    }
return 0;
}

One

题目描述
终于都走了。
曾经有 n-1 个人在他身边,然而现在只剩他一个人。
Who are you?Who am I?Why am I here?
走的越来越慢,人越来越少,可终于还是只剩一个了呢。
他们围成一圈,随机了一个人作为 1 号,然后逆时针依次编号。1号开始报数,报到 1,他走了;然后 2 号开始报数,2 号报了 1,3 号报了 2,于是 3 号也走了……每一轮都从上一次出局的下一个人开始报数,第 i 轮从 1 报到 i,报 i 的人出局。
直到只剩他一个人。却早已不记得他自己是谁。
他想知道他最初的号码,那上面承载着最初的记忆和最原始的愿望。
输入描述
第一行一个数 T 表示数据组数。
接下来 T 行,每行一个整数 n。
输出描述
共 T 行,每行一个数表示他原本的编号。
输入样例
2
2
3
输出样例
2
2
数据范围
对于%100 的数据,T≤10
对于%10 的数据,n≤5
对于%30 的数据,n≤3000
对于%60 的数据,n≤10000
对于%100 的数据,n≤10^7

约瑟夫问题
要事先了解一下如何线性推约瑟夫

最后得出的就是F(i)=(F(i-1)+M)%i,F(i)代表报i次数剩下的人是谁,M是每多少个人走一个

不同的是M是变化的,特殊处理一下就可以

关于公式怎么推的,请戳这里

#include<ctype.h>
#include<cstdio>
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do {c=getchar();if(c=='-') f=-1;} while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
int n,x,T;
int main(){
    T=read();
    while(T--){
        n=read();
        x=0;
        for(int i=2;i<=n;i++) x=(n-i+1+x)%i;
        printf("%d\n",x+1);
    }
return 0;
}

Phi

这里写图片描述
这里写图片描述

数论题,事实上就是个爆搜..

ans=pkiipki+1i+1....pkjj ,还给出 x=Πipki1i(pi1) ,我们只要枚举 pi 和相应的 ki 来想方设法拼出一个ans就可以了..

注意大质数的情况

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define N 100005
#define int long long
using namespace std;
int n,m,cnt,pri[N];
bool b[N];
long long ans;
bool check(int x){
    int sz=(int)sqrt(x);
    for(int i=1;pri[i]<=sz;i++) if(!(x%pri[i])) return 0;
    return 1;
}
void dfs(int k,int now,long long sum){
    if(sum>=ans) return;
    if(now==1){ans=sum;return;}
    if(now>m && check(now+1)) ans=min(ans,sum*(now+1));
    for(int i=k+1;pri[i]-1<=m;i++){
        if(pri[i]-1>now) break;
        if(!(now%(pri[i]-1))){
            int x=now/(pri[i]-1);
            long long y=sum*pri[i];
            dfs(i,x,y);
            while(!(x%pri[i])){
                x/=pri[i]; y*=pri[i];
                dfs(i,x,y);
            }
        }
    }
}
main(){
    scanf("%d",&n);m=(int)sqrt(n);
    if(n==1){
        printf("1");
        return 0;
    }
    for(int i=2;i<=50000;i++){
        if(!b[i])pri[++cnt]=i;
        for(int j=1;j<=cnt&&i*pri[j]<=50000;j++){
            b[i*pri[j]]=true;
            if(!(i%pri[j])) break;
        }
    }
    ans=2147483648ll;
    dfs(1,n,1);
    if(ans<=2147483647) printf("%lld",ans);
    else printf("-1");
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值