A、解碼
題解:
這題比較推式子簡單
問題就出在對n快速分解質因數
然後腦殘寫了個Pollard_Rho,和暴力一樣慢了。。。。
其實在數學一本通裏還有一種分解質因數的方法叫Fermat方法
設一個數有兩個質因子p,q(p<q),設q=p+y
則n=p*(p+y)=p^2+py
解一下二次方程得
p=(-y+sqrt(y^2+4n))/2
如果要使p是整數,那麼右邊的式子也得是整數
令a=y/2,b=sqrt(y^2+4n)/2=sqrt(a^2+n)
則p=b-a,q=b+a
由於a,b都是整數
所以有b^2=a^2+n
對於前三個subtask可以枚舉a,最後一個可以從ceil(sqrt(n))開始枚舉b
解出p,q後用exgcd求個逆元在快速冪一下就結束了
時間複雜度O(T*100)左右吧
代碼:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
inline LL gi()
{
char c;LL num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
LL mul(LL x,LL y,LL mod)
{
LL ret=x*y-(LL)((long double)x/mod*y+0.5)*mod;
return ret<0?ret+mod:ret;
}
LL ksm(LL x,LL y,LL mod)
{
LL ret=1;x%=mod;
while(y){
if(y&1)ret=mul(ret,x,mod);
y>>=1;x=mul(x,x,mod);
}
return ret;
}
void exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b){x=1;y=0;return;}
exgcd(b,a%b,y,x);
y-=(a/b)*x;
}
int main()
{
freopen("rsa.in","r",stdin);
freopen("rsa.out","w",stdout);
srand(3993991);
int T;LL n,m,c,phi,inv,tmp,x;
LL a,b,pr1,pr2,sq;
scanf("%d",&T);
while(T--){
n=gi();m=gi();c=gi();
//Format
if(n>1000000000000000000ll){
b=(LL)(sqrt(1.0*n)+0.9999999);
for(;;b++){
a=b*b-n;sq=(LL)sqrt(1.0*a);
if(sq*sq==a)
break;
}
a=sq;
}
else{
for(a=0;;a++){
b=a*a+n;sq=(LL)sqrt(1.0*b);
if(sq*sq==b)
break;
}
b=sq;
}
if(a>b)swap(a,b);
pr1=b-a;pr2=b+a;
//printf("bug:%lld %lld\n",pr1,pr2);
phi=(pr1-1)*(pr2-1);
exgcd(c,phi,inv,tmp);
inv=(inv%phi+phi)%phi;
x=ksm(m,inv,n);
printf("%lld\n",x);
}
}
B、排列
題解:
神題
考場上寫了個假模擬退火,直接爆零
後來發現自己是判斷無解判錯了。。。
各種調參之後終於拿了二十分暴力。。。
超級爬山代碼:(20分)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 505
#define LL long long
const LL fm=10000000000ll;
LL a[N][N],b[N],sum[N],p[N];
inline LL rean(LL n)
{
if(n==0)return 0;
return (1ll*rand()<<17|1ll*rand())%n;
}
double rs[N];
int main()
{
//freopen("permutation.in","r",stdin);
//freopen("permutation.out","w",stdout);
srand(123);
int n,i,j;double retmp,su=0.0;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%lf",&retmp);
rs[i]=retmp;
b[i]=fm*retmp;
}
sort(rs+1,rs+n+1);
for(i=1;i<=n;i++){
su+=rs[i];
if(i<n&&su<i*(i+1)/2-1e-8){
printf("-1");
return 0;
}
}
if(fabs(su-n*(n+1)/2)<1e-8){
printf("-1");
return 0;
}
LL pre=fm,dis=0;
for(i=1;i<n;i++){
p[i]=rean(pre);
pre-=p[i];
}
p[n]=pre;
for(i=1;i<=n;i++){
for(j=1;j<=n;j++)a[i][j]=j;
random_shuffle(a[i]+1,a[i]+n+1);
for(j=1;j<=n;j++)
sum[j]+=a[i][j]*p[i];
}
for(i=1;i<=n;i++)
dis=max(dis,abs(sum[i]-b[i]));
int T=2500000,op,h,x,y;
LL now=dis,dta;
double tuihuo=2;
while(T--){
tuihuo*=0.999999;
tuihuo=max(1.0,tuihuo);
//for(i=1;i<=10000000;i++);
//printf("T:%d\n",T);
//for(i=1;i<=n;i++){
// printf("%.10f",(double)p[i]/fm);
// for(j=1;j<=n;j++)
// printf(" %lld",a[i][j]);
// printf("\n");
//}
op=rand()%5;
dis=0;
for(j=1;j<=n;j++)
dis=max(dis,abs(sum[j]-b[j]));
//printf("%lld\n",dis);
LL tmpdis=dis;
if(op==0){
x=rand()%(n-1)+1;y=rand()%(n-1)+1;if(y==x)y=y%n+1;
if(p[x]<p[y])swap(x,y);
dta=rean(min(now,min(p[x],fm-p[y])));
for(j=1;j<=n;j++){
sum[j]-=a[x][j]*p[x];
sum[j]-=a[y][j]*p[y];
}
p[x]-=dta;p[y]+=dta;
for(j=1;j<=n;j++){
sum[j]+=a[x][j]*p[x];
sum[j]+=a[y][j]*p[y];
}
dis=0;
for(j=1;j<=n;j++)
dis=max(dis,abs(sum[j]-b[j]));
if((double)dis>=tuihuo*tmpdis){
dis=0;
for(j=1;j<=n;j++){
sum[j]-=a[x][j]*p[x];
sum[j]-=a[y][j]*p[y];
}
p[x]+=dta;p[y]-=dta;
for(j=1;j<=n;j++){
sum[j]+=a[x][j]*p[x];
sum[j]+=a[y][j]*p[y];
}
dis=tmpdis;
}
}
else if(op==2||op==3){
x=rand()%(n-1)+1;y=rand()%(n-1)+1;if(y==x)y=y%n+1;
for(h=1;h<=n;h++){
sum[x]-=a[h][x]*p[h];sum[y]-=a[h][y]*p[h];
swap(a[h][x],a[h][y]);
sum[x]+=a[h][x]*p[h];sum[y]+=a[h][y]*p[h];
}
dis=0;
for(j=1;j<=n;j++)
dis=max(dis,abs(sum[j]-b[j]));
if((double)dis>=tuihuo*tmpdis){
for(h=1;h<=n;h++){
sum[x]-=a[h][x]*p[h];sum[y]-=a[h][y]*p[h];
swap(a[h][x],a[h][y]);
sum[x]+=a[h][x]*p[h];sum[y]+=a[h][y]*p[h];
}
dis=tmpdis;
}
}
else{
h=rand()%(n-1)+1;
x=rand()%(n-1)+1;y=rand()%(n-1)+1;if(y==x)y=y%n+1;
sum[x]-=a[h][x]*p[h];sum[y]-=a[h][y]*p[h];
swap(a[h][x],a[h][y]);
sum[x]+=a[h][x]*p[h];sum[y]+=a[h][y]*p[h];
dis=0;
for(j=1;j<=n;j++)
dis=max(dis,abs(sum[j]-b[j]));
if((double)dis>=tuihuo*tmpdis){
sum[x]-=a[h][x]*p[h];sum[y]-=a[h][y]*p[h];
swap(a[h][x],a[h][y]);
sum[x]+=a[h][x]*p[h];sum[y]+=a[h][y]*p[h];
dis=tmpdis;
}
}
now=dis;
}
dis=0;
for(j=1;j<=n;j++)
dis=max(dis,abs(sum[j]-b[j]));
//printf("delta:%.10f\n",(double)dis/fm);
printf("%d\n",n);
for(i=1;i<=n;i++){
printf("%.10f",(double)p[i]/fm);
for(j=1;j<=n;j++)
printf(" %lld",a[i][j]);
printf("\n");
}
//for(i=1;i<=n;i++)
// printf("%.10f ",(double)sum[i]/fm);
//printf("\n");
}
下面是官方題解:
大致做法是對原序列排一個序,每次都用1 2 3……n這樣的排列去逼近它
我們二分一下這個排列的權值mid,如果原序列每個數xi減去mid*i之後,重新排序,如果還滿足條件2,那麼這個mid就是可行的
每次都用最大的mid作爲當前排列的權值,這樣我們每次就可以把原不等式中的一個大於等於變成等於
而且這種方法是保留了所有的等號的(並不是很明白。。。),就不會把等於變成小於
所以我們只需要n次就可以得到最後的答案
聽別人說有點卡精度,我調了好久精度,結果發現又是無解判錯了。。。
AC代碼:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 505
double eps=1e-10;
struct node{
double x;int id;
bool operator < (const node &t)const{
return x<t.x;
}
}a[N],tmp[N];
int n,b[N][N];double p[N];
double psum;
bool check(double mid)
{
for(int i=1;i<=n;i++){
tmp[i].x=a[i].x-mid*i;
if(tmp[i].x<-eps)return 0;
}
sort(tmp+1,tmp+n+1);double su=0.0;
for(int i=1;i<=n;i++){
su+=tmp[i].x;
if(su<1.0*(i+1)*i/2*(psum-mid)-eps)return 0;
}
return 1;
}
int main()
{
//freopen("permutation.in","r",stdin);
//freopen("permutation.out","w",stdout);
int i,j;double su=0.0;
scanf("%d",&n);
for(i=1;i<=n;i++){scanf("%lf",&a[i].x);a[i].id=i;}
sort(a+1,a+n+1);
for(i=1;i<=n;i++){
su+=a[i].x;
if(su<1.0*(i+1)*i/2-eps){printf("-1\n");return 0;}
}
if(fabs(su-1.0*n*(n+1)/2)>eps){printf("-1\n");return 0;}
psum=1;
for(i=1;i<=n;i++){
double l=0,r=psum,mid;
//for(j=1;j<=n;j++)
// printf("(%.6f,%d)",a[j].x,a[j].id);
//printf("\n");
while(r-l>eps){
mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
for(j=1;j<=n;j++){
b[i][a[j].id]=j;
a[j].x=a[j].x-l*j;
}
sort(a+1,a+n+1);
p[i]=l;psum-=p[i];
}
printf("%d\n",n);
for(i=1;i<=n;i++){
printf("%.12f",p[i]);
for(j=1;j<=n;j++)
printf(" %d",b[i][j]);
printf("\n");
}
}
C、安排
題解
先分數規劃二分一下
主要講講m=4的情況
我們對x數組進行差分,再對A,B數組求前綴和,他們的Σ(A-kB)*x是不變的
此時差分數組x的限制就從遞減變成了大於等於0
我們的目的就是分配x的大小,使得這三個Σ都取到正數
那麼一個xi對應了3個係數,如果將這三個係數寫成三維向量的形式
我們的目的就是使這些向量數乘上一個非負實數之後的和落於第一象限(x,y,z>0)
經過一番思考後,我們發現只需要存在兩個向量所構成的平面與第一象限半超平面有交集即可
其它的向量的係數直接賦爲0即可
我們的目的就轉化爲了判斷所有的向量是不是都可以落在一個過原點的半超平面下(是的話就無解了)
我們知道三維中一個半超平面的方程是ax+by+z<=0
也就是隻需要兩個變量就可以確定一個半超平面
我們再把這兩個變量想象成二維座標上的一個點(此時二維座標上一個點就代表了一個半超平面)
而我們的所有的三維向量(u,v,w)都會對這個半超平面做出一個限制
即:au+bv<=-w,而這種限制在一個二維平面上就表示了一個半平面
我們最後就將問題轉化爲了二維平面上求半平面交,判斷其是否爲空的問題了
代碼:(分類討論真噁心。。。)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
const double inf=1e10;
const double eps=1e-13;
int n,K,a[4][N],b[N];
void solve2()
{
double s0=0.0,s1=0.0,mx=0.0;
for(int i=1;i<=n;i++){
s0+=a[0][i];s1+=a[1][i];
if(fabs(s0)<eps){printf("1.000000000000\n");return;}
else mx=max(mx,s1/s0);
}
printf("%.12f\n",mx);
}
bool check3(double mid)
{
double u,v,mx=-inf,mi=inf;
for(int i=1;i<=n;i++){
u=(double)a[1][i]-mid*a[0][i];
v=(double)a[2][i]-mid*a[0][i];
if(u>=0&&v>=0)return 1;
if(fabs(u)<eps){if(-v<0)return 1;}
else{
if(u<0)mx=max(mx,-v/u);
else mi=min(mi,-v/u);
}
}
if(mi<mx||mi<0)return 1;
return 0;
}
struct point{
double x,y;
point(){}
point(double s,double t){x=s;y=t;}
point operator + (const point &t)const{return point(x+t.x,y+t.y);}
point operator - (const point &t)const{return point(x-t.x,y-t.y);}
point operator * (const double &t)const{return point(x*t,y*t);}
double operator *(const point &t)const{return x*t.y-y*t.x;}
}P[N];
int dcmp(double x){return x<-eps?-1:x>eps?1:0;}
struct line{
point S,T;double ang;
line(){}
line(point A,point B){
S=A;T=B;
if(dcmp(A.x-B.x)||dcmp(A.y-B.y))
ang=atan2(B.y-A.y,B.x-A.x);
}
bool operator < (const line &t)const{
return dcmp(ang-t.ang)?ang<t.ang:(t.T-S)*(T-S)>0;
}
}L[N],q[N];
int cnt;
point jiaodian(line A,line B)
{
A.T=A.T-A.S;B.T=B.T-B.S;
return A.S+(A.T*((B.S-A.S)*B.T/(A.T*B.T)));
}
bool banpingmianjiao()
{
L[++cnt]=line(point(0,0),point(inf,0));
L[++cnt]=line(point(inf,0),point(inf,inf));
L[++cnt]=line(point(inf,inf),point(0,inf));
L[++cnt]=line(point(0,inf),point(0,0));
sort(L+1,L+cnt+1);
int ql=0,qr=-1,i;
for(i=1;i<=cnt;i++){
if(i==1||(dcmp(L[i].ang-L[i-1].ang))){
for(;qr-ql+1>=2&&(jiaodian(q[qr],q[qr-1])-L[i].S)*(L[i].T-L[i].S)>eps;qr--);
for(;qr-ql+1>=2&&(jiaodian(q[ql],q[ql+1])-L[i].S)*(L[i].T-L[i].S)>eps;ql++);
q[++qr]=L[i];
}
}
for(;qr-ql+1>=3&&(jiaodian(q[qr],q[qr-1])-q[ql].S)*(q[ql].T-q[ql].S)>eps;qr--);
for(;qr-ql+1>=3&&(jiaodian(q[ql],q[ql+1])-q[qr].S)*(q[qr].T-q[qr].S)>eps;ql++);
if(qr-ql+1<=2)return 0;
return 1;
}
bool check4(double mid)
{
cnt=0;
double u,v,w;
for(int i=1;i<=n;i++){
u=(double)a[1][i]-mid*a[0][i];
v=(double)a[2][i]-mid*a[0][i];
w=(double)a[3][i]-mid*a[0][i];
if(u>=0&&v>=0&&w>=0)return 1;
if(dcmp(u)||dcmp(v)){
if(!dcmp(u)){
double tmp=-w/v;
if(v>0)L[++cnt]=line(point(1,tmp),point(-1,tmp));
else L[++cnt]=line(point(-1,tmp),point(1,tmp));
}
if(!dcmp(v)){
double tmp=-w/u;
if(u>0)L[++cnt]=line(point(tmp,-1),point(tmp,1));
else L[++cnt]=line(point(tmp,1),point(tmp,-1));
}
else{
if(v>0)L[++cnt]=line(point(1,(-w-u)/v),point(-1,(-w+u)/v));
else L[++cnt]=line(point(-1,(-w+u)/v),point(1,(-w-u)/v));
}
}
}
if(!banpingmianjiao())return 1;
else return 0;
}
int main()
{
freopen("arrangement.in","r",stdin);
freopen("arrangement.out","w",stdout);
int i,j;double l=0,r=1e11,mid;
scanf("%d%d",&n,&K);
for(i=1;i<=n;i++)scanf("%d",&a[0][i]);
for(i=1;i<=n;i++){
scanf("%d",&b[i]);b[i]--;
swap(a[0][i],a[b[i]][i]);
}
if(K==2){solve2();return 0;}
for(i=1;i<=n;i++)for(j=0;j<=3;j++)a[j][i]+=a[j][i-1];
bool flg;
//r=1;
while(r-l>eps){
mid=(l+r)*0.5;
if(K==3)flg=check3(mid);
if(K==4)flg=check4(mid);
if(flg)l=mid;
else r=mid;
}
printf("%.10f",l);
}