2019银川网络赛ABCDFH

这场打的非常坐牢。。除了签到题。赛时一题想不出来。。纯纯坐牢五个小时。但是题目还不错,值得一补。

A-Maximum Element In A Stack

思路:用题目给出的函数生成操作。这题用multiset来模拟,o(nlogn)是TLE的。需要o(n)的做法--->维护单调栈。当push元素比栈顶小时,直接再次push栈顶元素。当push元素比栈顶大时,才正常push.这样操作,栈顶永远是栈中最大的元素。也可以正常pop。

//unsigned int 不能 改成 unsigned long long!!!!!!!!!
// 这题会产生负数,unsigned int 会溢出导致出现负数。 但是unsigned long long 不会因为溢出而出现负数,导致两者取模后的值不一样。
//应该遵循原本题目给出的代码!!!
long long n, p, q, m,ans=0;         //ans要long long!!!!
unsigned int SA, SB, SC;
unsigned int rng61(){
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}
void gen(){
    stack<long long> stk;          //这里也要开long long!!!
    for(int i = 1; i <= n; i++){
        if(rng61() % (p + q) < p) {
            unsigned int num=rng61() % m + 1;   //num为unsigned int!!!
            if(stk.size()==0 || num>stk.top()) stk.emplace(num);    //栈为空 or num 大于栈顶,push->num;
            else stk.emplace(stk.top());   //否则维护栈,push->stk.top();
            ans^=i*stk.top();
        }
        else if(stk.size()){
            stk.pop();
            if(stk.size()) ans^=i*stk.top();
        }
    }
}
void solve(){                  //A--Maximum Element In A Stack    //multiset--o(nlogn)会TLE..
    // 那只能o(n)!--单调栈! 如果当前push的数字比栈顶小,直接再push栈顶。否则正常push这个更大的数。这样维护出来的栈,栈顶永远是所有栈元素的最大值。
    int t0; cin>>t0;
    for(int tt=1;tt<=t0;tt++){
        ans=0;
        cin>>n>>p>>q>>m>>SA>>SB>>SC;
        gen();
        cout<<"Case #"<<tt<<": "<<ans<<endl;
    }
}

ps:这题不能define int long long..需要完全按照题目给出的来。

B-Rolling The Polygon

思路:点Q的运动轨迹为所有弧长L的总和.

α和θ为互补关系。可以根据余弦定理cosx=(a*a+b*b-c*c)/(2*a*b)算出cosθ=-cosα。再通过acos(cosθ)算出角度θ.

L=|θ|*r--r为Q和P2(旋转中心点)的距离.

//一开始想着直接算外角,但是会非常麻烦,因为下一个点的位置可能在左上,右上,左下,右下,或者正上,正下。分类非常麻烦。
//但是可以算内角,内角的计算是固定的。然后利用互补角,就知道外角是什多少。
int n;
pair<double,double> pos[150];
double xx,yy;
double cal(int idx){
    double x1,y1,x2,y2,x3,y3;
    x1=pos[idx].first,y1=pos[idx].second;
    if(idx==n) x2=pos[1].first,y2=pos[1].second;
    else x2=pos[idx+1].first,y2=pos[idx+1].second;
    if(idx==n) x3=pos[2].first,y3=pos[2].second;
    else x3=pos[idx+2].first,y3=pos[idx+2].second;
    double r=sqrt( (xx-x2)*(xx-x2) + (yy-y2)*(yy-y2) );
    double a=sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
    double b=sqrt( (x2-x3)*(x2-x3) + (y2-y3)*(y2-y3) );
    double c=sqrt( (x1-x3)*(x1-x3) + (y1-y3)*(y1-y3) );
    double cosx=(-a*a-b*b+c*c)/(2*a*b);         //因为要的是外角,互补关系,所以加负号
    double du=acos(cosx);
    return r*du;
}
void solve(){         //B--Rolling The Polygon
    int t0; cin>>t0;
    for(int t=1;t<=t0;t++){
        cin>>n;
        for(int i=1;i<=n;i++) {
            cin>>pos[i].first>>pos[i].second;
            pos[i+n]=pos[i];            //idx+1,idx+2可能会超出n。
        }
        cin>>xx>>yy;
        double ans=0;
        for(int i=1;i<=n;i++) ans+=cal(i);
        ans*=1000,ans=round(ans),ans/=1000;  //四舍五入,保留三位小数
        cout<<fixed<<setprecision(3)<<"Case #"<<t<<": "<<ans<<endl;
    }
}

C-Caesar Cipher

思路:签到题。转为字母的偏移量处理即可。

void solve(){               //C--Caesar Cipher签到题--转为'数字'(偏移量)处理
    int t0; cin>>t0;
    for(int t=1;t<=t0;t++){
        int n,m; cin>>n>>m;
        string str0,str1,res;
        cin>>str0>>str1>>res;
        int dif=str0[0]-str1[0];
        int num[55];
        for(int i=0;i<m;i++) num[i]=res[i]-'A';         //'Z'的偏移量是25!!
        for(int i=0;i<m;i++){
            if(dif>0) num[i]=(num[i]+dif)%26;   //res应该右移
            if(dif<0){                          //res应该左移
                num[i]+=dif;
                if(num[i]<0) num[i]+=26;         //'Z'的偏移量是25!!
            }
        }
        cout<<"Case #"<<t<<": ";
        for(int i=0;i<m;i++) {
            char c='A'+num[i];
            cout<<c;
        }
        cout<<endl;
    }
}

D-Take Your Seat

非常非常有意思的思维+概率题。切记切记,这题只有疯子一个人不知道座位,其他人的位置如果没有被占,是一定可以坐到正确位置的。

知乎--疯子问题

①疯子坐到了正确的位置。后面的人也一定坐到正确位置。问题结束。

②疯子坐错了位置,但不是最后一个位置:

假设疯子坐到了k号位置,1<k<n。那么2到k-1号都可以坐到自己的位置上。原本座位为k的人进来后,发现位置没有了,他就重新随机选择一个位置。

注意!!也就是说,在这种情况下,除了n变小了,这个问题仍然是疯子问题。只是问题规模从n,变为n-k+1,而现在这个k号乘客就是新的疯子。

而对于每一个疯子,他应该坐到的位置是第一个疯子的位置。

③疯子坐到了最后一个人的位置上。问题结束。

可以发现--第二个选项是无效的,它只是把问题规模缩小了,实质上没有做任何选择。所以实质上只有①和③两种选择。所以答案的结果为1/2;

更深入的思考-------如果在坐座位的中间,只要有一个人坐到了一号疯子的位置,最后一个人一定可以坐到他自己正确的位置上。

所以!!实质上,最后一个人的位置只有两个:疯子的位置 or 自己的位置。

知乎上还有数学推导的证明方法。。

csdn题解

思路:在知道第一个疯子问题的答案总是1/2之后。第二个问题:上飞机的顺序是随机的,同样只有一号乘客是不知道座位的。

所以概率P=1/m+(1/2)*(m-1)/m;

其中1/m为疯子最后一个上,那么前面的人一定能坐到正确的位置上。疯子必然坐到自己的正确位置上。

其中(1/2)*(m-1)/m--(m-1)/m为疯子不是最后一个上的概率,1/2为第一问最后乘客坐到自己位置的概率。

只要疯子不是最后一个上,无论疯子什么时候上,最后一个乘客坐到自己位置的概率都是1/2。因为疯子上的早晚只会影响疯子问题的规模大小,而疯子问题的规模大小是不影响结果的,都是1/2.

int n,m;
void solve(){           //D--Take Your Seat        //非常非常非常非常有意思的题!!!!!!!!!!!!
    int t0; cin>>t0;
    for(int t=1;t<=t0;t++){
        cout<<"Case #"<<t<<": ";
        cin>>n>>m;
        double res2=0;
        if(n==1) cout<<"1.000000 ";
        else cout<<"0.500000 ";
        if(m==1) cout<<"1.000000"<<endl;
        else{
            res2=1.0/m+(double)(m-1)/(2.0*m);
            //疯子最后一 个上--1/m
            //疯子不是第一个上--(1/2)*( (m-1)/m )
            res2*=1000000,res2=round(res2),res2/=1000000;
            cout<<fixed<<setprecision(6)<<res2<<endl;
        }
    }
}

F-Moving On

csdn题解

思路:排序+floyd+dp思想。

dp[k][i][j]:定义为考虑前k个点的情况下,i到j的最短距离为dis.

对danger排序是为了查询的时候,满足第x个点的危险度,那么前面的也一定满足。

状态转移方程:dp[k][i][j]=min(dp[k-1][i][j],dp[k-1][i][node]+dp[k-1][node][j]);  min(不经过node点中转,经过node点中转).

int n,q;
pair<int,int> danger[205];
int dis[205][205][205];   //dp思想:k,i,j--含义为,考虑前k个点的情况下,i到j的最短距离为dis.
const int inf=0x3f3f3f3f;
// --先对输入的点的危险度进行小到大排序,之后跑floyd,后面满足的点,前面也一定满足。
void floyd(){
    for(int k=1;k<=n;k++){
        int cur=danger[k].second;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                ////dis=min(不用这个点中转,用这个点中转)!!
                dis[k][i][j]=min(dis[k-1][i][j],dis[k-1][i][cur]+dis[k-1][cur][j]);
            }
        }
    }
}
void solve(){                   //F--Moving On    //!!排序!!+floyd+dp
    int t0; cin>>t0;
    for(int t=1;t<=t0;t++){
        cout<<"Case #"<<t<<":"<<endl;
        cin>>n>>q;
        memset(dis,inf,sizeof(dis));    //init
        for(int i=1;i<=n;i++){
            cin>>danger[i].first;
            danger[i].second=i;
        }
        sort(danger+1,danger+n+1);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                cin>>dis[0][i][j];
            }
        }
        floyd();
        while(q--){
            int u,v,w; cin>>u>>v>>w;
            int minn=INT_MAX;
            for(int k=0;k<=n;k++){
                if(danger[k].first<=w&&dis[k][u][v]<minn) minn=dis[k][u][v];
            }
            cout<<minn<<endl;
        }
    }
}

H-Fight Against Monsters

思路:"性价比"贪心。先杀“性价比”高的,即 atk/杀死回合 高的先杀。因为这里没有“金钱”的限制,所以“性价比”的贪心是没问题的!!

//H题一直贪一直贪,一直没贪出来。。
// 其实想法是之前错误的想法,把背包dp误以为贪心的那种贪心。
//就是-某题拥有一定的钱,买牛奶尽可能多的牛奶。不同牛奶有对应的 价格和kg。
//之前一开始是想贪心先买“性价比”高的,就是 “kg/价格” 大的。但是这种想法是错的,这个应该是背包dp,因为要尽可能用完拥有的钱
typedef struct myp{
    int hp,atk;
    double ave;
}myp;
bool cmp(myp a,myp b){
    if(a.ave!=b.ave) return a.ave>b.ave;
    return a.hp<b.hp;
}
myp arr[100005];            //定义在全局,不然栈溢出
//H--权值贪心,类似上面的一开始的贪心想法--先杀“性价比”高的,即 atk/杀死回合 高的先杀。因为这里没有“金钱”的限制,所以“性价比”的贪心是没问题的!!
void solve(){                 //H--Fight Against Monsters
    int t0; cin>>t0;
    for(int t=1;t<=t0;t++){
        int n,sum=0,ans=0; cin>>n;
        for(int i=1;i<=n;i++){
            cin>>arr[i].hp>>arr[i].atk;
            int l=1,r=10000,res=1;                  //r得到1e4,1e3不够大
            while(l<=r){
                int mid=(l+r)>>1;
                if(mid*mid+mid>=2*arr[i].hp){
                    res=mid;
                    r=mid-1;
                }
                else l=mid+1;
            }
            arr[i].hp=res,sum+=arr[i].atk;
            arr[i].ave=1.0*arr[i].atk/arr[i].hp;
        }
        sort(arr+1,arr+n+1,cmp);
        for(int i=1;i<=n;i++) ans+=sum*arr[i].hp,sum-=arr[i].atk;
        cout<<"Case #"<<t<<": "<<ans<<endl;
    }
}

ps:过题人数从多到少:CBHADF-G,G题是树上背包。练练之后马上补G!

 

posted @ 2024-04-26 23:57  osir  阅读(4)  评论(0编辑  收藏  举报