2019, XII Samara Regional Intercollegiate Programming Contest 解题报告

CodeForces 652F. Ants on a Circle 题目链接http://codeforces.com/problemset/problem/652/F思路题意是说一个环上有几只蚂蚁,有各自的运动方向,然后蚂蚁碰撞会立刻改变方向,问你t秒之后各个蚂蚁的位置。这是一个经典的蚂蚁碰撞问题。有两点重要结论:首先,蚂蚁碰撞可以视作没有碰撞,穿过去了,不过编号会发生改变。其次,蚂蚁的相对位置不变。所以,t秒之后,蚂蚁的分布我们很容易就可以计算出来,然后只要确定 阅读详情

2019 XII Samara Regional Intercollegiate Programming Contest

传送门


A. Rooms and Passages

题意:

有 n + 1 n+1 n+1 个房间, n n n 把钥匙,有第 i i i 把钥匙你可以从房间 i − 1 i-1 i−1 走到房间 i i i。每个房间 i i i 都有两种类型中的一种:

  • 一、只有拥有钥匙 a i a_i ai​ 才能从 i − 1 i-1 i−1 走到 i i i。
  • 二、从 i − 1 i-1 i−1 走到 i i i 之后会失去钥匙 a i a_i ai​ 。

现在问你从每个房间出发开始往后走,最远能走几个房间。

题解:

尺取(two pointers),根据走到房间的种类:如果是种类一,则判断所需钥匙数量是否为 1;如果是种类二,则增加当前房间消耗的钥匙数量,并且左端点退出时减少被消耗的钥匙数量。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=500005;
int a[maxn];
int type[maxn];
int tag[maxn];
int ans[maxn];
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        if(x>0) a[i]=x,type[i]=1;
        else a[i]=-x,type[i]=2;
    }
    int ed=1;
    for(int st=1;st<=n;st++){
        while(ed<=n){
            if(type[ed]==1){
                if(tag[a[ed]]) break;
            }else{
                tag[a[ed]]++;
            }
            ed++;
        }
        ans[st]=ed-st;
        if(type[st]==2) tag[a[st]]--;
    }
    for(int i=1;i<=n;i++) printf("%d%c",ans[i],(i==n)?'\n':' ');
    return 0;
}

B. Rearrange Columns

题意:

给定一个 2 ∗ l e n 2*len 2∗len 的点阵,每个点均为 . . . 或 # \# #,问能否将点阵的每一列重排,使得所有的 # \# # 构成一个联通块。

题解:

显然我们可以分析出,每一列只有第一行是 # \# # 的都可以放到一起,形如:

######
......

同理每一列只有第二行是 # \# # 的也都可以放到一起,形如:

......
######

而这两种情况同时出现的情况下,我们只需要存在某一列两行都是 # \# #,我们就可以把他们拼接成如下形式:

#######......
......#######

这样就成功将所有 # \# # 放到了同一个联通块中。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1005;
char s[2][maxn];
int cnt[4];
int main(){
    scanf("%s%s",s[0],s[1]);
    int len=(int)strlen(s[0]);
    for(int i=0;i<len;i++){
        if(s[0][i]=='#'&&s[1][i]=='.') cnt[0]++;
        else if(s[0][i]=='#'&&s[1][i]=='#') cnt[1]++;
        else if(s[0][i]=='.'&&s[1][i]=='#') cnt[2]++;
        else if(s[0][i]=='.'&&s[1][i]=='.') cnt[3]++;
    }
    if(cnt[0]&&cnt[2]&&!cnt[1]) printf("NO\n");
    else{
        printf("YES\n");
        for(int j=0;j<cnt[0];j++) printf("#");
        for(int j=0;j<cnt[1];j++) printf("#");
        for(int j=0;j<cnt[2];j++) printf(".");
        for(int j=0;j<cnt[3];j++) printf(".");
        printf("\n");
        for(int j=0;j<cnt[0];j++) printf(".");
        for(int j=0;j<cnt[1];j++) printf("#");
        for(int j=0;j<cnt[2];j++) printf("#");
        for(int j=0;j<cnt[3];j++) printf(".");
        printf("\n");
    }
    return 0;
}

C. Jumps on a Circle

题意:

有 p p p 个点 0 0 0 到 p − 1 p-1 p−1 围成一圈(即对于 1 < i < n 1 < i < n 1<i<n, i i i 的左边为 i − 1 i-1 i−1, i i i 的右边为 i + 1 i+1 i+1, 1 1 1 的左边为 n n n, n n n 的右边为 1 1 1),一个人初始站在 0 0 0,第一次跳 1 1 1 步,第二次跳 2 2 2 步,第 i i i 次跳 i i i 步,共跳 n n n 次,问总共有多少个点被跳到过(包括初始站过的点 0 0 0)。

题解:

数据给出的 n n n 大到 1 0 18 10^{18} 1018,当然我们不能全部跳完,所以我们思考会不会在某个地方出现循环节。而这种情况下的循环节要求到达同一个地方之后的所有行动都要一致。找规律可以发现当 p p p 为奇数时,跳跃 p p p 次即可找到循环节,思考原因我们可以发现,跳跃 p p p 次我们总共跳跃的步数是 ( 1 + p ) ∗ p 2 \frac{(1+p)*p}{2} 2(1+p)∗p​ ,其中 1 + p 1+p 1+p 为偶数,即 ( 1 + p ) % 2 = 0 (1+p)\%2=0 (1+p)%2=0,则 ( 1 + p ) ∗ p 2 % p = 0 \frac{(1+p)*p}{2}\%p=0 2(1+p)∗p​%p=0 成立,且下一次跳跃的步数为 p + 1 % p = 1 p+1\%p=1 p+1%p=1,满足循环节的条件。接下来思考为什么当 p p p 为偶数的时候循环节不是 p p p:同样,跳跃 p p p 步之后的总步数为 ( 1 + p ) ∗ p 2 \frac{(1+p)*p}{2} 2(1+p)∗p​,此时 p p p 为偶数, ( 1 + p ) ∗ p 2 % p = 0 \frac{(1+p)*p}{2}\%p=0 2(1+p)∗p​%p=0 不成立。而我们想要使式子成立的方法也很简单,将等式左边的 p p p 替换为 2 p 2p 2p 即可,这样式子变成 ( 1 + 2 p ) ∗ 2 p 2 % p = ( ( 1 + p ) ∗ p ) % p = 0 \frac{(1+2p)*2p}{2}\%p=((1+p)*p)\%p=0 2(1+2p)∗2p​%p=((1+p)∗p)%p=0,显然是成立的。所以实际上当 n n n 过大时,我们只需要跳最多 2 p 2p 2p 步即可解决问题。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=10000007;
bool tag[maxn*2];
int main(){
    ll p,n;scanf("%lld%lld",&p,&n);
    n=min(n,p*2);
    memset(tag,false, sizeof(tag));
    int ans=0,tmp=0;
    ans++;tag[0]=true;
    ll sum=0;
    for(int i=1;i<=n;i++){
        tmp++;
        if(tmp>=p) tmp-=p;
        sum+=tmp;
        if(sum>=p) sum-=p;
        if(!tag[sum]){
            tag[sum]=true;
            ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}

D. Country Division

题意:

给出一棵 n n n 个点的树,接下来有多组询问,每次询问给出一种树上点的染色方式(部分点染红色,部分点染蓝色,部分点布染色),要求将树分成森林,使得所有红色点在同一棵子树上,所有蓝色点在同一棵子树上,且红色点和蓝色点不在同一棵树上。每组询问独立。

题解:

未通过


E. Third-Party Software - 2

题意:

给出 n n n 条线段,问能否找出一些线段覆盖整个区间 [ 1 , m ] [1,m] [1,m],如果能的话找出需要线段数量最少的方案。

题解:

贪心,对线段进行排序,排序规则为左端点小的排在前面,若左端点相同则右端点大的排在前面。将当前位置设置为上一次右端点+1,继续从上一次不符合条件的线段开始往后找,找到所有左端点 ≤ \leq ≤ 当前点的线段中右端点最大的一条,更新当前点。当当前点 ≥ m + 1 \geq m+1 ≥m+1时,说明所有已选择的线段已经可以覆盖整个区间了。

正确性:这样能找到的一定是当前情况下左端点最大且右端点最大的,若无法找到,则无论如何选择线段都不可能满足覆盖整个区间。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=200005;
struct node{
    int l,r,id;
    bool operator<(const node &t)const{
        if(l!=t.l) return l<t.l;
        else return r>t.r;
    }
}p[maxn];
int ans[maxn],tot;
int main(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++){
        p[i].id=i+1;
        scanf("%d%d",&p[i].l,&p[i].r);
    }
    sort(p,p+n);
    int now=1,mx=0,ed=0;
    while(true){
        bool flag=false;
        int tmp=0;
        for(;ed<n;ed++){
            if(p[ed].l>now) break;
            flag=true;
            if(p[ed].r>mx){
                mx=p[ed].r;
                tmp=ed;
            }
        }
        if(!flag||mx<now) break;
        ans[tot++]=p[tmp].id;
        now=mx+1;
        if(now==m+1) break;
    }
    if(now!=m+1) printf("NO\n");
    else{
        printf("YES\n");
        printf("%d\n",tot);
        for(int i=0;i<tot;i++) printf("%d%c",ans[i],(i==tot-1)?'\n':' ');
    }
    return 0;
}

F. Friendly Fire

题意:

共有 n n n 只怪兽,每只有一个攻击力 a a a 和血量 h h h,现在你可以选择其中的两只怪兽自相残杀。假设你选择两个怪兽为 x x x 和 y y y,若 a x ≥ b y a_x \geq b_y ax​≥by​ 则怪兽 b b b 死亡,若 b x ≥ a y b_x \geq a_y bx​≥ay​ 则怪兽 a a a 死亡(两个事件可以同时触发)。问你最少会受到多少伤害(所有最后活着的怪兽的攻击力之和),并且在这种情况下选择的是哪两只怪兽。

题解:

首先我们可以对每个怪兽的血量从小到大排序,这样我们对每个怪兽 i i i 的攻击力 a i a_i ai​ 就可以在怪兽中二分,知道哪些怪兽是可以被怪兽 i i i 打死的(即 h p o s ≤ a i h_{pos} \leq a_i hpos​≤ai​ 中 p o s pos pos 的界限)。这样保证了选择的怪兽一定是可以被怪兽 i i i 打死的,剩下要确认的事只有能被怪兽 i i i 打死的怪兽中是否存在某一只能打死怪兽 i i i(即 ∃ a j ≥ h i , 1 ≤ j ≤ p o s \exists a_j \geq h_i, 1\leq j \leq pos ∃aj​≥hi​,1≤j≤pos 是否成立)。显然我们只需要在 [ 1 , p o s ] [1,pos] [1,pos] 中找到一个最大的攻击力 a a a 即可判断这件事,所以我们用线段树维护。

而细节是在查询最大攻击力前要把怪兽 i i i 从线段树中删去,查询完之后要重新加回去,因为怪兽 i i i 本身可能会出现 a i ≥ h i a_i \geq h_i ai​≥hi​ 的情况。

代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=300005;
struct node{
    int a,h,id;
    bool operator<(const node &t)const{
        return h<t.h;
    }
}p[maxn];
int mx[maxn<<2],idx[maxn<<2],mxtmp,idtmp;
vector<int> vec;
void pushup(int rt){
    if(mx[rt*2]>mx[rt*2+1]){
        mx[rt]=mx[rt*2];
        idx[rt]=idx[rt*2];
    }else{
        mx[rt]=mx[rt*2+1];
        idx[rt]=idx[rt*2+1];
    }
}
void build(int l,int r,int rt){
    if(l==r){
        mx[rt]=p[l].a;
        idx[rt]=p[l].id;
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
    pushup(rt);
}
void update(int l,int r,int rt,int pos,int val){
    if(l==r){
        mx[rt]=val;
        return;
    }
    int mid=(l+r)/2;
    if(pos<=mid) update(l,mid,rt*2,pos,val);
    else update(mid+1,r,rt*2+1,pos,val);
    pushup(rt);
}
void query(int l,int r,int rt,int L,int R){
    if(L<=l&&r<=R){
        if(mx[rt]>mxtmp){
            mxtmp=mx[rt];
            idtmp=idx[rt];
        }
        return;
    }
    int mid=(l+r)/2;
    if(L<=mid) query(l,mid,rt*2,L,R);
    if(mid<R) query(mid+1,r,rt*2+1,L,R);
}
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
        p[i].id=i;
        scanf("%d%d",&p[i].a,&p[i].h);
    }
    sort(p+1,p+1+n);
    for(int i=1;i<=n;i++) vec.push_back(p[i].h);
    build(1,n,1);
    int mxx=0,x=1,y=2;
    for(int i=1;i<=n;i++){
        int id=(int)(lower_bound(vec.begin(),vec.end(),p[i].a+1)-vec.begin());
        if(!id) continue;
        mxtmp=0;idtmp=1;
        update(1,n,1,i,0);
        query(1,n,1,1,id);
        update(1,n,1,i,p[i].a);
        int tmp=mxtmp;
        if(mxtmp>=p[i].h) tmp+=p[i].a;
        if(tmp>mxx){
            mxx=tmp;x=p[i].id;y=idtmp;
        }
    }
    printf("%d\n%d %d\n",mxx,x,y);
    return 0;
}


G. Akinator

题意:

太乱了没看懂

不理解。

题解:

未通过。


H. Missing Number

题意:

交互题。

给出 n n n,总共有 0 − n 0-n 0−n 共 n + 1 n+1 n+1 个数,现在给出一个大小为 n n n 的数组 a a a,数组中的元素各不相同,现在希望你进行至多 2 n + 19 2n+19 2n+19 次询问求出缺少的一个元素的值。每次询问的格式为 ? i b,即询问 a i a_i ai​ 二进制位第 b b b 位是 0 0 0 还是 1 1 1。得知答案后的输出方法为 ! x, x x x 即为求出的答案。

题解:

已知 n n n 后可以求出 0 − n 0-n 0−n 中有多少个数二进制最低位为 0 0 0 和多少个数二进制最低位为 1 1 1,进行 n n n 次询问,求出数组 a a a 中这两类值的数量,可以知道哪一类数少了一个。在缺少的那一类继续枚举下一个二进制位,直到对所有二进制位做完操作。

因为这样每次能筛掉一半的数,所以询问次数为 n + n 2 + n 2 2 + . . . + 1 n+\frac{n}{2}+\frac{\frac{n}{2}}{2}+...+1 n+2n​+22n​​+...+1,即不会超过 2 n 2n 2n 次。

代码:

#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
vector<int> vec,veco,vece;
int main(){
    ios_base::sync_with_stdio(false);
    int n;cin>>n;
    for(int i=1;i<=n;i++) vec.push_back(i);
    int bit=0;
    for(int tmp=n;tmp;tmp/=2){
        bit++;
    }
    int now=0;
    for(int i=0;i<bit;i++){
        int tmp=1<<(i+1);
        int etmp=now;
//        int otmp=(1<<i)+now;
        int e=(n+1)/tmp+((n+1)%tmp>etmp);
//        int o=(n+1)/tmp+((n+1)%tmp>otmp);
        for(int j=0,sz=(int)vec.size();j<sz;j++){
            cout<<'?'<<' '<<vec[j]<<' '<<i<<endl;
            int x;cin>>x;
            if(!x) vece.push_back(vec[j]);
            else veco.push_back(vec[j]);
        }
        if(vece.size()<e){
            vec=vece;
        }else{
            vec=veco;
            now+=1<<i;
        }
        vece.clear();veco.clear();
    }
    cout<<'!'<<' '<<now<<endl;
    return 0;
}

I. Painting a Square

题意:

你有一个初始全白的 a ∗ a a*a a∗a 的矩阵,此时在矩阵左上角放置一个 b ∗ b b*b b∗b 的红色矩阵,这个红色矩阵只能平移(即水平或垂直移动)。问这个红色矩阵最少移动多少步可以使得整个白色矩阵都被染红(初始可以认为左上角 b ∗ b b*b b∗b 的矩阵已经被染红)。

题解:

分析可得红色矩阵应当螺旋移动才能使得步数最少,而给定矩阵边长相同,在纸上模拟直接找到规律即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int main(){
    int a,b;scanf("%d%d",&a,&b);
    ll tmp=a-b;
    ll ans=tmp;
    while(tmp>0){
        ans+=tmp+tmp;
        tmp-=b;
    }
    printf("%lld\n",ans);
    return 0;
}

J. The Power of the Dark Side - 2

题意:

给出 n n n 个三元组,每个三元组有三个元素 a , b , c a,b,c a,b,c。每个三元组可以调整自己 a , b , c a,b,c a,b,c 的值,要求是 a + b + c a+b+c a+b+c 的和不变,且 a , b , c a,b,c a,b,c 都不能是负数(即要满足 a , b , c ≥ 0 a,b,c \geq 0 a,b,c≥0)。

给出三元组比较大小的方法:三元组 x x x 比三元组 y y y 大的条件为 x a > y a , x b > y b , x c > y c x_a > y_a , x_b > y_b , x_c > y_c xa​>ya​,xb​>yb​,xc​>yc​ 三者满足至少两个。

询问对于每个三元组 i i i 比多少个其他三元组大。

题解:

假设某个三元组 x x x 的三个元素为 a , b , c a,b,c a,b,c,其中两个较小的为 a , b a,b a,b,则对于某个三元组 y y y 的三个元素为
{ a + 1 , b + 1 , 0 } \{a+1,b+1,0\} {a+1,b+1,0},我们可以得出 y > x y > x y>x 成立。
由此我们可知:如果 y a + y b + y c − 2 > m i n { x a , x b , x c } + m i d { x a , x b , x c } y_a+y_b+y_c-2 > min\{x_a,x_b,x_c\} + mid\{x_a,x_b,x_c\} ya​+yb​+yc​−2>min{xa​,xb​,xc​}+mid{xa​,xb​,xc​} ,可以得到 y > x y>x y>x,其中 m i n min min 为取最小值, m i d mid mid 为取第二小值。

所以我们要做的事就是对每个三元组求一个 a + b + c − 2 a+b+c-2 a+b+c−2 和 m i n { x a , x b , x c } + m i d { x a , x b , x c } min\{x_a,x_b,x_c\} + mid\{x_a,x_b,x_c\} min{xa​,xb​,xc​}+mid{xa​,xb​,xc​},并按后者排序,再对每个三元组二分答案即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=500005;
struct node{
    int id;
    ll sum,mn;
    bool operator<(const node &t)const{
        return mn<t.mn;
    }
}p[maxn];
ll a[3];
int ans[maxn];
int main(){
    int n;scanf("%d",&n);
    for(int i=0;i<n;i++){
        p[i].id=i;
        p[i].sum=0;
        for(int j=0;j<3;j++){
            scanf("%lld",&a[j]);
            p[i].sum+=a[j];
        }
        sort(a,a+3);
        p[i].mn=a[0]+a[1]+2;
    }
    sort(p,p+n);
    for(int i=0;i<n;i++){
        int l=0,r=n-1;
        while(l<=r){
            int mid=(l+r)/2;
            if(p[mid].mn<=p[i].sum){
                ans[p[i].id]=mid;
                if(mid<i) ans[p[i].id]++;
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
    }
    for(int i=0;i<n;i++) printf("%d%c",ans[i],(i==n-1)?'\n':' ');
    return 0;
}

K. Deck Sorting

题意:

给你一个只包含 R , G , B R,G,B R,G,B 三种元素的字符串,并且给你两个栈,你可以从左向右遍历字符串,将每个元素放入两个栈的其中一个,问最后能否将某个栈放到另一个栈上,使得所有相同的元素都相邻,如:
对于串 RGBRGB,栈的内容变化过程为:

    |     |     |     | G   | G B |
    |     |   B | R B | R B | R B |
R   | R G | R G | R G | R G | R G |

最后将右边的堆放到左边的堆上,结果为:

B
B
G
G
R
R

满足所有相同元素都相邻。

题解:

因为只有三个元素,所以我们只需要枚举三个元素的顺序,方案只有六种。不妨假设我们只会把第二个堆放在第一个堆的上方,则此时第一个元素必定在第一个堆的下部,第三个元素必定在第二个堆的上部,而位于中间的第二个元素会出现在第一个堆的上部和第二个堆的下部。接下来我们讨论对于每种某种情况字符串是否可能取到满足的方法(因为六种情况的本质做法是一样的):

假设当前要求的重排结果为 RGB:

  • 当字符串最前面为 R , G R,G R,G 元素相互交替时,我们一定能把这一部分分别放到两个堆的底部。

  • 而当串中第一次出现 B B B 时,我们一定会将这个 B B B 放在第二个堆的顶部,自此之后,第二个堆就只能放 B B B 了,
    而第一个串的顶部仍为 R R R,所以此时我们能接受的字符串内容为 R , B R,B R,B 元素相互交替。

  • 情况会再次发生变化,当字符串再一次出现 G G G 时,由于第二个堆的顶部已经出现了 B B B,所以这个 G G G 只能放到第一个堆的堆顶,自此之后,第一个堆就只能放 G G G 了,而第二个串的顶部仍为 B B B,所以此时我们能接受的字符串内容为 B , G B,G B,G 元素相互交替。

看懂上面的情况后就可以解决每个状态了。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1005;
char s[maxn];
int len;
char t[5]="RGB";
int cnt[3],a[3];
bool check(){
    int tmp1=-1;
    for(int i=0;i<len;i++){
        if(s[i]==t[a[2]]){
            tmp1=i;
            break;
        }
    }
    if(tmp1==-1) return true;
    int tmp2=-1;
    for(int i=tmp1+1;i<len;i++){
        if(s[i]==t[a[1]]){
            tmp2=i;
            break;
        }
    }
    if(tmp2==-1) return true;
    for(int i=tmp1+1;i<tmp2;i++){
        if(s[i]==t[a[1]]) return false;
    }
    for(int i=tmp2+1;i<len;i++){
        if(s[i]==t[a[0]]) return false;
    }
    return true;
}
int main(){
    scanf("%s",s);
    len=(int)strlen(s);
    for(int i=0;i<len;i++){
        for(int j=0;j<3;j++){
            if(s[i]==t[j]) cnt[j]++;
        }
    }
    for(int i=0;i<3;i++) a[i]=i;
    do{
        if(check()){
            printf("YES\n");
            return 0;
        }
    }while(next_permutation(a,a+3));
    printf("NO\n");
    return 0;
}

L. Inscribed Circle

题意:

给出两个圆的坐标与半径,保证两个圆恰好交于两点,求相交部分里能放下的最大圆的坐标与半径。

题解:

简单几何题。

分析可得内部圆的圆心处于两个给定圆的圆心连线上,接下来要做的只是计算坐标与半径。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int main(){
    double x1,y1,r1,x2,y2,r2;scanf("%lf%lf%lf%lf%lf%lf",&x1,&y1,&r1,&x2,&y2,&r2);
    if(x1>x2) swap(x1,x2),swap(y1,y2),swap(r1,r2);
    double L=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
    double r=(r1+r2-L)/2;
    double x=(L-r2+r)/L*(x2-x1)+x1;
    double y=(L-r2+r)/L*(y2-y1)+y1;
    printf("%.10f %.10f %.10f",x,y,r);
    return 0;
}

M. Shlakoblock is live!

题意:

有 n n n 个游戏,每个游戏有一个快乐值 p p p,并且每个游戏有一个已经被投的票数 v v v。现在问你你可以给哪些游戏投票,最大化快乐值的期望值(对于每个游戏你只能投一票)。

题解:

可以分析出快乐值的期望值为 ∑ i = 1 n p i ∗ v i ∑ i = 1 n v i \frac{\sum_{i=1}^{n}{p_i*v_i}}{\sum_{i=1}^{n}{v_i}} ∑i=1n​vi​∑i=1n​pi​∗vi​​,当你给某个游戏 j j j 投票后,快乐值得期望值会变为 ( ∑ i = 1 n p i ∗ v i ) + p j ( ∑ i = 1 n v i ) + 1 \frac{(\sum_{i=1}^{n}{p_i*v_i})+p_j}{(\sum_{i=1}^{n}{v_i})+1} (∑i=1n​vi​)+1(∑i=1n​pi​∗vi​)+pj​​。
所以只需要对 p p p 进行排序,每次判断选取后能否使期望值变大即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=1005;
struct node{
    ll p,v;
    int id;
    bool operator<(const node &t)const{
        return p>t.p;
    }
}a[maxn];
int ans[maxn],tot;
int main(){
    int t;scanf("%d",&t);
    while(t--){
        tot=0;
        int n;scanf("%d",&n);
        ll up=0,down=0;
        for(int i=1;i<=n;i++){
            a[i].id=i;
            scanf("%lld%lld",&a[i].p,&a[i].v);
            up+=a[i].p*a[i].v;
            down+=a[i].v;
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++){
            ll uptmp=up+a[i].p;
            ll downtmp=down+1;
            if(uptmp*down>=up*downtmp){
                up=uptmp;
                down=downtmp;
                ans[tot++]=a[i].id;
            }else{
                break;
            }
        }
        ll g=__gcd(up,down);
        up/=g;down/=g;
        printf("%lld/%lld\n",up,down);
        printf("%d\n",tot);
        sort(ans,ans+tot);
        for(int i=0;i<tot;i++) printf("%d%c",ans[i],(i==tot-1)?'\n':' ');
    }
    return 0;
}
银河麒麟鲲鹏aarch64架构安装docker和K8s 鲲鹏云服务器上安装docker-云社区-华为云 (huaweicloud.com) 这里我下载的版本是docker-18.09.3.tag docker 安装包地址:https://download.docker.com/linux/static/stable 配置system服务 在github对应分支的contrib/init/systemd/ 下面,提供了对应的docker.service以及docker.socket文件,可以直接拷贝到本地文件使用: 对应链接:https://github.c 阅读详情

相关推荐

基于MATLAB/Simulink与WSL2的ROS2实时联合仿真实践

本文详细介绍了基于MATLAB/Simulink与WSL2的ROS2实时联合仿真实践。通过环境配置、PX4仿真环境搭建、自定义消息生成、Simulink通信实现及实时优化等步骤,提供了完整的无人机位置控制案例,帮助开发者实现高精度的实时联合仿真。

red88的博客 203

2019, XII Samara Regional Intercollegiate Programming Contest 全部题解

英语巨烂的我,把两个签到题读成了不可写题…感觉给我一个中文题面,有机会ak… A. Rooms and Passages 题意:有 n + 1个点在一排,有 n 条边连接,依次求点 i 往 点 n 的方向走最多能走多远,如果当前的边权值为 x (x > 0),且已经走过一条权值为 -x 的边,那么这条边不能走。 思路:倒着处理,标记每条正边在当前出现的最小坐标即可。 #include<...

ccsu_cat 1787

西门子S7-200SMART控制步进电机(五)

前面详细讲述了运动控制向导的配置和运动控制指令的使用方法,接下来要讲的是简单程序的编写和调试。 在编写程序时,需要注意的是自定义的符号和地址不能与运动控制向导分配的存储器冲突。 一、软硬件组成 1、软件:STEP-7-Micro/WIN SMART软件; 2、硬件:西门子S7-200 SMART ST40 PLC、57步进电机和驱动器(DM542)。 二、手动控制步进电机旋转方向和速度 具体步骤...

qq_43134642的博客 2万+

2019, XII Samara Regional Intercollegiate Programming Contest

A. Rooms and Passages 题目描述:把一个序列分为两类,ai>0和ai<0, 如果在 i 点门口需要第一类通行证且持有第一类通行证可以直接从i-1点走向 i 点 且无其他影响,如果 i 点门口需要第二类通行证可以从i-1点走向i点,并使 | ai | 这个通行证失效。假设一开始处于 i 点,并拥有所有种类的通行证(种类指 | ai | ) ,问从i 点走向n 点最多能走几步? 题目一开始没读懂,不过这道题总感觉读起来别扭。 这道题比较好想的思路就是从前往后遍历,遇到的负数存其

weixin_50909982的博客 409

串级PID 位置环+速度环

串级PID 位置环+速度环

weixin_62775618的博客 9109

Windows系统下LaTeX环境搭建与编辑器配置全攻略

本文详细介绍了在Windows系统下搭建LaTeX环境的完整流程,包括TeX Live的安装、TeXStudio编辑器的配置优化以及常见问题排查。特别针对中文用户推荐使用清华镜像源加速下载,并提供了XeLaTeX编译器配置、中文支持等实用技巧,帮助用户快速上手LaTeX文档编写。

weixin_42554408的博客 449

C - Jumps on a Circle(考数学应用)

There are p points on the circle numbered from 0 to (p - 1) in a way that from the point 0 you can move to the point 1, from the point 1 — to the point 2, and so on, and from the point (p - 1) you ca...

weixin_43797452的博客 574

Cisco思科 电子科技大学计算机网络 SRWE 期末技能考试 (PTSA)

电子科技大学2024春_SRWE_计算机网络系统 SRWE 期末技能考试 (PTSA)

2301_76745065的博客 5094

X Samara Regional Intercollegiate Programming Contest

题目链接 [题目](http://codeforces.com/gym/101341 "X Samara Regional Intercollegiate Programming Contest") [非官方代码](https://github.com/luqmanarifin/cp/tree/master/X%20Samara%20Regional%20Intercollegiate%20Programming

idealism_xxm的专栏 1475

【android bluetooth 案例分析 01】【HiCar 详解 3】【hicar时如何交换Wifi参数的】

如果直接使用 L2CAP(不经过 RFCOMM),会需要额外的协议来保证可靠性,而 RFCOMM 天然支持。BLE 广播的数据长度受限(31 字节 / 255 字节扩展),不适合传输复杂的 Wi-Fi 连接参数。使用 RFCOMM 传输参数,可以兼容不同厂商的车机和手机,而无需依赖特殊的蓝牙功能。手机解析这些参数,并使用 Wi-Fi Direct 连接到车机。,然后才能让 Wi-Fi Direct 进行真正的数据传输。,适合传输 Wi-Fi 连接参数等关键数据。,可以安全地交换 Wi-Fi 连接信息。

奔跑吧 android 的博客 1740

强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解

强化学习从基础到进阶-常见问题和面试必知必答[7]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解

丨汀、的博客 511

2019, XII Samara Regional Intercollegiate Programming (G、H、J、K、L题解)

G题 https://codeforces.com/gym/102215/problem/G 题意 有一天黑客德米特里发现了一个有趣的游戏叫Akinator。这个游戏的规则很简单:玩家想一些名人,阿克熄灯器试着在有限的几个问题中猜出这个人。每个回合熄灯器都会问玩家:“这个人是i1还是i2 ?”还是我?(这里m是问题中的人数,每个问题的人数都不一样)。玩家回答“是”或“不是”,阿克熄...

weixin_30907935的博客 322

杰理SDK入门教程(五):按键使用配置

摘要 杰理SDK提供IO按键和AD按键两种方案。IO按键通过GPIO电平检测实现,配置时需要设置板级宏定义和硬件连接,默认使用PB1引脚。AD按键基于ADC电压采样,通过电阻分压区分按键,需配置功能使能、硬件通道和键值适配。按键功能通过修改SDK中的key_table文件实现。两种方案适用于不同硬件场景,开发者可根据需求选择。

qq_42014563的博客 775

XI Samara Regional Intercollegiate Programming Contest Problem L. Queries on a String 预处理

A string s is given. Also there is a string p, and initially it is empty. You need to perform q operations of kind «add a letter to the end of the string p» and «remove a letter from the end of the s...

loooooog的博客 672

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

IX Samara Regional Intercollegiate Programming Contest F 三分

链接:戳这里 F. Two Points time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard output There are two points (x1, y1) and (x2, y2) on the plane. Th

Cross 782

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

CF 2018, XI Samara Regional Intercollegiate Programming Contest 习题总结

题目:A. Restoring Numbers Pavel had two positive integers a and b. He found their sum s and greatest common divisor g, and forgot a and b after that. Help him to restore the original numbers. Input A ...

小博客 997

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

XI Samara Regional Intercollegiate Programming Contest Problem C. Third-Party Software

Pavel is developing a game. To do that, he needs functions available in a third-party library too famous to be called. It is known that the function i first appeared in version ai and existed until v...

loooooog的博客 342

XI Samara Regional Intercollegiate Programming Contest Russia, Problem A.Restoring Numbers

Pavel had two positive integers a and b. He found their sum s and greatest common divisor g, and forgot a and b after that. Help him to restore the original numbers. Input A single line contains tw...

loooooog的博客 230

【2020, XIII Samara Regional Intercollegiate Programming Contest】B. Bonuses on a Line

B. Bonuses on a Line 题意:数轴上有 (n) 个点,坐标为 ((x_i,0)) ,你从原点开始能够移动 (t) 个单位距离,询问最多能够经过几个点。 分析:将正点和负点分开存,然后枚举我们到达的左端点(右端点同理),二分搜索从该端点向右走最多能够经过几个正点。 #include <bits/stdc++.h> #define ll long long using n...

qq_43328587的博客 944

2018, XI Samara Regional Intercollegiate Programming Contest 解题报告

2018, XI Samara Regional Intercollegiate Programming Contest 传送门 A. Restoring Numbers 题意: 给出两个数 aaa 和 bbb 的和 sss 和最大公因数 ggg,要你求出任意一组 aaa 和 bbb 的解。 题解: 我们可以知道 gcd(a,b)=ggcd(a,b)=ggcd(a,b)=g,则我们假设 a=p∗...

Stupid_Turtle的博客 814

C - Jumps on a Circle

题目大意 一个环,从0开始到p-1,开始位于0的位置,第一次走1步,第二次走2步,第三次走3步… 问n步后走过了多少个不同的点? 题解 通过简单模拟发现,2p步后回到原点 如果n大于2p,那么相当于2p+1,2p+2…那么和原来的又相同了 意思就是2p范围内是有效的,超过了就一直重复之前的行为。 就算是0步,原来的点是0,也是1个不同的点。 #include <iostream> #include <cmath> using namespace std; #define ll l

名字不能相同 131

hdu 4669 Mutiples on a circle(高效)

题目链接:hdu 4669 Mutiples on a circle代码#include <cstdio> #include <cstring> #include <algorithm>using namespace std; typedef long long ll; const int maxn = 50005;int N, K, A[maxn<<1], C[maxn<<1]; int dp[2

不慌不忙、不急不躁 707

Gym-102215K Deck Sorting

题意:在字符串中抽出一个子序列,然后和剩下的序列拼在一起,使得相同的字符是连续的。 题记:要使三个字符连续只有6种情况 {‘R’,‘G’,‘B’},{‘R’,‘B’,‘G’}, {‘G’,‘R’,‘B’},{‘G’,‘B’,‘R’}, {‘B’,‘G’,‘R’},{‘B’,‘R’,‘G’} 这样我们可以先选全部第一个字符放进第一个序列中,全部第三个字符放进第二个序列中,然后把第二个字符放在第一个序...

moyangxian 477

A - Rooms and Passages Gym - 102215A

There are (n+1)(n+1) rooms in the dungeon, consequently connected by nn passages. The rooms are numbered from 00 to nn, and the passages — from 11 to nn. The ii-th passage connects rooms (i−1)(i−1) an...

weixin_44694282的博客 1899

Jump(递推)

4727 - Jump Asia - Seoul - 2009/2010 Integers 1, 2, 3,..., n are placed on a circle in the increasing order as in the following figure. We want to construct a sequence from these numbers on a circ...

Simone chou... 183

UVA1452 Jump

倒推

ACoder 387
上一篇: Hdu2196-Computer (树形dp)(经典题)
下一篇: 2018, XI Samara Regional Intercollegiate Programming Contest 解题报告
Stupid_Turtle
博客等级 码龄9年 25粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值