这场打的非常坐牢。。除了签到题。赛时一题想不出来。。纯纯坐牢五个小时。但是题目还不错,值得一补。
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 自己的位置。
知乎上还有数学推导的证明方法。。
思路:在知道第一个疯子问题的答案总是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
思路:排序+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!