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;
}
LabVIew串口收发的一些问题 LabView串口收发实验总结 阅读详情

相关推荐

Multisim 11.0的安装破解汉化步骤

15:打开注册机,选择管理员运行,默认第一项,选择生成文件路径,这里不要随意选择,要找到你安装Multisim安装路径下Shard下的License Manager路径下,否则会报错,破解失败。14:后面会出现一个更新提示,选择否,然后选择重启电脑后,先不要选择重启电脑,在这里出现了问题(可能敲击回车,默认去重启电脑了),所以我说不要重启电脑,重启破解时右下角出现这,点击否。17:重启电脑,如果前面重启过,那可以跳过重启,应用程序下找到下图所示,启动。16:点击确认覆盖,路径下生成了该文件。

qq_53260940的博客 2371

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

RK3588多路MIPI摄像头采集与WiFi低延迟传输实战指南

本文详细介绍了基于RK3588平台实现多路MIPI摄像头同步采集并通过WiFi进行低延迟传输的完整实战方案。文章深入剖析了从V4L2驱动配置、多线程采集、环形缓冲区设计到UDP网络传输优化的核心技术,旨在帮助开发者在工业检测、智能安防等场景下构建高并发、高实时性的视频流系统,实现端到端延迟控制在100毫秒以内的目标。

tensor9flow的博客 786

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

通达信短周期指标盘中实时监控与预警选股实战

本文详细介绍了通达信短周期指标在盘中实时监控与预警选股中的实战应用。通过分时指标改造、多股同列监控技巧及预警策略优化,帮助短线交易者捕捉转瞬即逝的交易机会,提升选股效率与准确性。重点解析了如何避免假信号干扰及系统卡顿问题,并探讨了从预警到自动交易的进阶应用。

weixin_29230805的博客 894

OTP/EEPROM 读取与处理

Camera OTP/EEPROM 读取与处理 文章目录Camera OTP/EEPROM 读取与处理1. OTP概念1.1 基本概念1.2 OTP存储类型1.3 OTP烧录信息类型1.4 OTP 作用2. 配置准备2.1 基本认识2.2 OTP/EEPROM配置3. OTP/EEPROM datasheet理解 以及 读取方式3.1 data sheet 内容3.2 一般OTP/EEPROM(e...

Understand-image 1万+

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

高斯-约当(Gauss-Jordan)消元法——c++代码实现

具体算法的详细原理请参照此篇博客:高斯-约当(Gauss-Jordan)消元法下面是具体c++代码,不保证程序最优化,提供一个可行的程序,供大家改进,同时感谢大家提意见。——大神勿喷呀,我是一个小菜鸟~void Guss_Jordan(int n,double *a)//阶数n 系数矩阵a { double*a_temp = new double[n*n * 2];//a与单位矩阵的组合

zwz的学习之路 6873

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

改进YOLOv9的CT图像肺结节检测与分类算法:从原理到实战

本文将详细介绍如何改进YOLOv9算法,使其更好地适应CT图像肺结节检测与分类任务。我们将从数据集准备、模型改进、训练优化到最终部署,完整呈现一个端到端的深度学习项目,并提供完整的代码实现。

YOLO 188

微信小程序 - 获取用户头像昵称等信息(带授权检测)

前言 接口调整官方文档:https://developers.weixin.qq.com/community/develop/doc/0000a26e1aca6012e896a517556c01 在以前,是否授权可以直接弹出询问用户,但现在微信更改了策略,必须由用户点击按钮触发。下面将用代码来实现用户授权及获取用户信息。 功能介绍 无论确认授权还是拒绝授权,都会跳转对应页面,不让用户卡在界面。 点击授权后,下次不在出现授权页面了,如果拒绝,下次依然出现。 以下代码可直接复制粘贴,具体功能如下:

🏆 前端领域优质创作者 1万+

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

嵌入式系统开发笔记112:通过有人云测试MQTT

项目中使用到“有人”公司的WH-LTE-7S0模块,本文讲解通过有人云进行MQTT测试的方法。

snmplink的博客 997

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

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

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

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

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

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

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、付费专栏及课程。

余额充值