P76
他
【问题描述】
一张长度为�的纸带, 我们可以从左至右编号为0 − �(纸带最左端标号为
0)。 现在有�次操作, 每次将纸带沿着某个位置进行折叠, 问所有操作之后纸带
的长度是多少。
【输入格式】
第一行两个数字�, �如题意所述。
接下来一行�个整数代表每次折叠的位置。
【输出格式】
一行一个整数代表答案。
【样例输入】
5 2
3 5
【样例输出】
2
【样例解释】
树上有只鸟。
【数据规模与约定】
对于60%的数据, �, � ≤ 3000。
对于100%的数据, � ≤ 1018, � ≤ 3000。
去年在刷题班的时候就会,但是全忘了,现场推的。。。
还是写出来了,注意要开 unsigned long long
注意看N辣么大,M辣么小,我们肯定要从M上搞点事情。维护一个l,r表示当前的左右端点。要点有三
- 当该折叠点在l-r区间的左伴部分的时候,之后的折叠点从左边向右边映射,l=该折叠点
- 当该折叠点在l-r区间的右伴部分的时候,之后的折叠点从右边向左边映射,r=该折叠点
- 注意开unsigned long long 才可以
#include<iostream>
#include<cstdio>
#define ULL unsigned long long
using namespace std;
ULL f[3005],n,L,R;
int m;
inline ULL read() {
ULL w=0,flag=1;
char ch=getchar();
while(ch>'9'||ch<'0') {
if(ch=='-')flag=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0') {
w=w*10+ch-'0';
ch=getchar();
}
return w*flag;
}
int main() {
freopen("he.in","r",stdin);
freopen("he.out","w",stdout);
n=read();
m=read();
L=0;
R=n;
for(int i=1; i<=m; i++)f[i]=read();
for(int i=1; i<=m; i++) {
if(f[i]*2>=L+R) R=f[i];// 舍掉右边
else L=f[i];// 舍掉左边
for(int j=i+1; j<=m; j++) {
if(f[j]>R) f[j]=R*2-f[j];
if(f[j]<L) f[j]=L*2-f[j];
}
}
cout<<R-L<<endl;
fclose(stdin);
fclose(stdout);
return 0;
}
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e5+10;
int a[MAXN],top;
int erfen(int x){
int l=1,r=top,Mid,ans=0;
while(l<=r){
Mid=l+r>>1;
if(a[Mid]>=x) ans=Mid,r=Mid-1;
else l=Mid+1;
}
return ans;
}
inline void read(int &x){
x=0; int f=1; char c=getchar();
while(c>'9'||c<'0'){ if(c=='-') f=-1; c=getchar(); }
while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar(); } x*=f;
}
int main(){
freopen("she.in","r",stdin);
freopen("she.out","w",stdout);
int i,j,k,n,m,s,l,r,x,ans,ans2,now,T;
bool flag;
read(T);
while(T--){
read(m),read(s),read(l),read(r);
if(l>=m){ printf("-1\n"); continue; }
if(r>=m) r=m-1;
now=0,flag=0,top=0;
for( n=1;n*n<=m;++n){
now=(now+s)%m;
if(l<=now&&now<=r){
printf("%d\n",n);
flag=1; break;
}
a[++top]=now;
}
--n;
int sta=a[top];
if(flag) continue;
sort(a+1,a+top+1);
for(now=1;now*n<=m;++now){
l=(l-sta+m)%m;r=(r-sta+m)%m;
if(l>r){
if(a[top]>=l){ flag=1; break; }
if(a[1]<=r) { flag=1; break; }
} else {
if(a[top]<l) continue;
int x=erfen(l);
if(a[x]<=r) { flag=1; break; }
}
}
if(!flag) { printf("-1\n"); continue; }
ans=now*n,now=0;
for(i=1;i<=top;++i){
now=(now+s)%m;
if(l<=now&&now<=r) break;
}
ans+=i;
printf("%d\n",ans);
}
return 0;
}
看的题解,二分答案,不过前边的一顿操作猛如虎,没看懂…….
它
【问题描述】
�个人坐成一圈, 其中第�个人拿着一个球。 每次每个人会以一定的概率向
左边的人和右边的人传球。 当所有人都拿到过球之后, 最后一个拿到球的人即为
胜者。 求第�个人获胜的概率。(所有人按照编号逆时针坐成一圈)
【输入格式】
第一行一个数�代表数据组数。
对于每组数据, 第一行两个整数�, �如题意所述。
接下来每行一个实数�代表该人将球传给右边的人的概率。
【输出格式】
对于每组数据, 一行一个实数代表答案, 保留9位小数。
【样例输入】
1
5 1
0.10
0.20
0.30
0.40
0.50
【样例输出】
0.007692308
【样例解释】
然后鸟是我的。
【数据规模与约定】
对于20%的数据, � ≤ 3。
对于70%的数据, �, � ≤ 100。
对于100%的数据, � ≤ 10000,1 ≤ � ≤ 100。
概率DP,p表示向右传的概率,q表示向左传球的概率,可以看做是在合并两个相邻的人,合并到最后就好处理了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LDB long double
#define DB double
const int MAXN = 1000+10;
int T,n,k,pre[MAXN],next[MAXN];
LDB p[MAXN],q[MAXN];
void del(int b){
int a=pre[b],c=next[b];
LDB pa=p[a],pb=p[b],pc=p[c];
p[a]=pa*pb/(1-pa*(1-pb)),q[a]=1-p[a];
q[c]=(1-pc)*(1-pb)/(1-pb*(1-pc));
p[c]=1-q[c];
next[a]=c,pre[c]=a;
}
LDB solve(){
if(n<=2) return 1;
if(n<=3) return k==1 ? p[1] : q[2];
for(int i=1;i<=n;++i) pre[i]=i-1,next[i]=i+1;
pre[1]=n,next[n]=1;
if(k==1){
for(int i=2;i<n-1;++i) del(i);
return p[1];
}
if(k==n-1){
for(int i=2;i<n-1;++i) del(i);
return q[n-1];
}
for(int i=2;i<n-1;++i)
if(i!=k) del(i);
del(k);
return q[k]*p[1]+p[k]*q[n-1];
}
int main(){
freopen("it.in","r",stdin);
freopen("it.out","w",stdout);
DB v;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)
scanf("%lf",&v),p[i]=v,q[i]=1-v;
printf("%.9lf\n",(DB)solve());
}
fclose(stdin);fclose(stdout);
return 0;
}