他
【问题描述】
一张长度为的纸带,我们可以从左至右编号为(纸带最左端标号为)。现在有次操作,每次将纸带沿着某个位置进行折叠,问所有操作之后纸带的长度是多少。
【输入格式】
第一行两个数字如题意所述。
接下来一行个整数代表每次折叠的位置。
【输出格式】
一行一个整数代表答案。
【样例输入】
5 2
3 5
【样例输出】
2
【样例解释】
树上有只鸟。
【数据规模与约定】
对于60%的数据,N,M<=3000.
对于100%的数据,N<=10^18,M<=3000.
【题目分析】
考场上用了并查集,就是把每次折叠后把这些点的父亲节点改为重合的最小的那个点,这样每一个点的父亲节点都是连续的,并查集啊,10^18你并查集个头啊...,无奈,脑子不好想不到正解
#include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; int n,m; int father[5001]; int len=0; int x; int Find(int q) //寻找根节点 { if(father[q]==q) return q; else return father[q]=Find(father[q]); } int main() { freopen("he.in","r",stdin); freopen("he.out","w",stdout); scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) father[i]=i; int l=0,r=n; for(int i=1;i<=m;i++) { scanf("%d",&x); int fa=Find(x); if(fa-l>=r-fa) { len=fa-l; for(int j=1;j<=r-fa;j++) father[fa+j]=father[fa-j]; r=fa; } else { len=r-fa; for(int j=1;j<=fa-l;j++) father[fa-j]=father[fa+j]; l=fa; } } printf("%d",len); fclose(stdin);fclose(stdout); return 0; }
事实上,用离线的做法每次折叠后把后面的折叠的编号都改变,取长的那段区间直接求......(一直理解错了那个a的含义.编号改变后都在长度之内..)
#include <cstdio> #include <cstring> #include <iostream> #define ll long long using namespace std; ll n; int m; ll a[3010]; ll abss(ll x) { return x<0?-x:x; } int main() { freopen("he.in","r",stdin); freopen("he.out","w",stdout); scanf("%I64d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%I64d",&a[i]); for(int i=1;i<=m;i++) { int flag=0; if(a[i]<(n+1)/2) flag=1; for(int j=i+1;j<=m;j++) { if(flag==1) a[j]=abss(a[i]-a[j]); else if(a[j]>a[i]) a[j]=a[i]*2-a[j]; } n=max(a[i],n-a[i]); } printf("%I64d",n); fclose(stdin);fclose(stdout); return 0; }