XOR和路径
既然是异或,就考虑每一位分开计算,最后等价于求每一位是 1 的概率,但这题他有重边和自环!直接转移是不行了就只能高斯消元了,设
F
u
F_u
Fu 为这一位是 1 的概率易得方程式
F
u
×
d
e
g
u
+
∑
(
u
,
v
)
∈
E
,
w
(
u
,
v
)
>
>
k
=
1
F
v
−
∑
(
u
,
v
)
∈
E
,
w
(
u
,
v
)
>
>
k
=
0
F
v
=
∑
(
u
,
v
)
∈
E
,
w
(
u
,
v
)
>
>
k
=
1
1
F_u\times deg_u+\sum_{(u,v)\in E,w(u,v)>>k=1}F_v-\sum_{(u,v)\in E,w(u,v)>>k=0}F_v=\sum_{(u,v)\in E,w(u,v)>>k=1}1
Fu×degu+(u,v)∈E,w(u,v)>>k=1∑Fv−(u,v)∈E,w(u,v)>>k=0∑Fv=(u,v)∈E,w(u,v)>>k=1∑1
最后答案
∑
k
=
1
m
a
x
u
2
k
×
F
1
\sum_{k=1}^{maxu}2^k\times F_1
k=1∑maxu2k×F1
typedef long double ld;
const int N=102;
const long double eps=1e-8;
struct edge{
int nxt,to,val;
}e[N*N<<1];
int head[N],cnte,du[N];
int n,m;
ld ans;
struct Gauss{
ld a[N];
ld b;
Gauss operator *(const ld x)const{
Gauss tmp;
tmp.b=b*x;
for(int i=1;i<=n;i++)
tmp.a[i]=a[i]*x;
return tmp;
}
Gauss operator -(const Gauss x)const{
Gauss tmp;
tmp.b=b-x.b;
for(int i=1;i<=n;i++)
tmp.a[i]=a[i]-x.a[i];
return tmp;
}
}f[35][N];
ld res[35][N];
void add(int u,int v,int w){
e[++cnte]=(edge){head[u],v,w};
head[u]=cnte;
++du[u];
}
inline void Swap(Gauss &a,Gauss &b){
Gauss tmp=a;
a=b,b=tmp;
}
void Gs(int w){
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j)
if(fabs(f[w][j].a[i])>eps){
if(j!=i) Swap(f[w][i],f[w][j]);
break;
}
if(fabs(f[w][i].a[i])<eps) return ;
for(int j=1;j<=n;j++){
if(j==i)continue;
ld y=f[w][j].a[i]/f[w][i].a[i];
f[w][j]=f[w][j]-f[w][i]*y;
}
}
for(int i=n;i;--i){
ld t=f[w][i].b;
for(int j=i+1;j<=n;++j) t-=res[w][j]*f[w][i].a[j];
res[w][i]=t/f[w][i].a[i];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
if(y!=x) add(y,x,z);
}
for(int x=1;x<n;++x){//n只进不出
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to,w=e[i].val;
for(int j=0;j<=30;j++){
if((w>>j)&1)
f[j][x].a[y]+=1.0/(1.0*du[x]),f[j][x].b+=1.0/(1.0*du[x]);
else f[j][x].a[y]-=1.0/(1.0*du[x]);
}
}
}
for(int x=1;x<=n;x++)
for(int j=0;j<=30;++j)
f[j][x].a[x]+=1.0;
for(int i=0;i<=30;++i)
Gs(i);
for(int i=0;i<=30;++i)
ans+=(1<<i)*1.0*res[i][1];
printf("%.3Lf",ans);
return 0;
}
数学作业
此题递推式很好得到
F
n
=
F
n
−
1
×
l
e
n
n
+
n
F_n=F_{n-1}\times len_n+n
Fn=Fn−1×lenn+n其中
l
e
n
n
len_n
lenn 为
n
n
n 的位数。但一看
n
n
n 的数据范围,
O
(
n
)
O(n)
O(n) 肯定过不了。那什么东西能加速递推式计算呢?就要用到矩阵了。根据递推式很好得到
[
F
n
n
+
1
]
=
[
F
n
−
1
n
]
×
A
\begin{bmatrix} F_{n} & n +1 \end{bmatrix}=\begin{bmatrix} F_{n-1} & n \end{bmatrix} \times A
[Fnn+1]=[Fn−1n]×A
由于
n
+
1
n+1
n+1 与
n
n
n 的常数转化,所以最终的式子
[
F
n
n
+
1
1
]
=
[
F
n
−
1
n
1
]
×
[
l
e
n
n
0
0
1
1
0
0
1
1
]
\begin{bmatrix} F_{n} & n +1 & 1 \end{bmatrix}=\begin{bmatrix} F_{n-1} & n & 1 \end{bmatrix} \times \begin{bmatrix} len_n & 0 & 0 \\ 1 & 1 & 0\\ 0 & 1 & 1 \end{bmatrix}
[Fnn+11]=[Fn−1n1]×⎣⎡lenn10011001⎦⎤
现在完了吗?没有!由于
l
e
n
n
len_n
lenn 的存在使得矩阵无法跨位数转化就只能分段转化了。
#define int __int128//虽然 long long 就可以了但没找到是哪爆了
int n,m;
void mul(int a[3],int b[3][3]){
int c[3];
memset(c,0,sizeof c);
for(int i=0;i<3;++i)
for(int j=0;j<3;++j){
c[i]=(c[i]+a[j]*b[j][i]%m)%m;
}
memcpy(a,c,sizeof c);
}
void muls(int a[3][3]){
int c[3][3];
memset(c,0,sizeof c);
for(int i=0;i<3;++i)
for(int j=0;j<3;++j)
for(int k=0;k<3;++k)
c[i][j]=(c[i][j]+a[i][k]*a[k][j]%m)%m;
memcpy(a,c,sizeof c);
}
signed main(){
read(n),read(m);
if(n==1) prt(1%m);
if(n==2) prt(12%m);
if(n==3) prt(123%m);
if(n==4) prt(1234%m);
if(n==5) prt(12345%m);
if(n==6) prt(123456%m);
if(n==7) prt(1234567%m);
if(n==8) prt(12345678%m);
if(n==9) prt(123456789%m);
if(n>=10){//为了使下面的 p 能乘10,1~9就特判了
int f[3]={123456789%m,10,1};
for(int p=10;n>=p;p*=10){
f[0]=(f[0]*p%m*10%m+p)%m;
f[1]=(f[1]+1)%m;
int a[3][3]={{p*10%m,0,0},{1,1,0},{0,1,1}};
int y=min(p*10-p-1,n-p);
while(y){
if(y&1) mul(f,a);
muls(a);
y>>=1;
}
}
prt(f[0]%m);
}
return 0;
}
数矩形
做这题的时候,一开始认为很简单,以为矩形只有横着的,结果样例都过不了,后来发现斜着也有矩形 ,这就引出了两个问题:
- 面积怎么保证精度
- 怎么确定四点是否为一个矩形
对于第一个,因为题上说输出一个非负整数,于是我就很自然的想到为什么答案一定是一个整数?想了很久最后还是推出来了,如下图。
这个推出来后,顺便也解决了精度的问题,就不用长乘宽,保证了答案正确。
第二个,就可以根据矩形的性质:对角线相等,对角线的中点坐标都是同一个。
于是我们可以先枚举出所有对角线,把每条对角线的长度(存平方),中点坐标(不除二),两个端点(为了求面积)。然后排完序后,可以组成矩形的对角线就自然而然挨在一起了。
#define N 1505
#define ll long long
int n,m;
ll ans;
struct node{
ll x,y;
ll a,b;
ll len;
}c[N],p[N*N];
bool cmpp(node u,node v){
if(u.x==v.x)
if(u.y==v.y)
return u.len<v.len;
else return u.y<v.y;
else return u.x<v.x;
}
signed main(){
read(n);
for(int i=1;i<=n;++i) read(c[i].x),read(c[i].y);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j){
m++;
p[m].len=abs(c[i].x-c[j].x)*abs(c[i].x-c[j].x)//长度
+abs(c[i].y-c[j].y)*abs(c[i].y-c[j].y);
p[m].x=c[i].x+c[j].x;//中点
p[m].y=c[i].y+c[j].y;
p[m].a=i;//两端点
p[m].b=j;
}
sort(p+1,p+m+1,cmpp);
for(int i=1,j;i<=m;++i,j=i){
while(p[i].len==p[++j].len){
if(p[i].x!=p[j].x || p[i].y!=p[j].y) continue;
ll o1=abs(c[p[i].a].x-c[p[j].a].x);
ll o2=abs(c[p[i].a].y-c[p[j].a].y);
ll o3=abs(c[p[i].b].y-c[p[j].a].y);
ll o4=abs(c[p[i].b].x-c[p[j].a].x);
ans=max(ans,o1*o3+o2*o4);
}
}
prt(ans);
}
任务调度
由于第三种的机器不超过十个,我们就可以先深搜枚举第三种机器状态,然后对于第一种机器按 b i b_i bi 从大到小排序,第二种机器按 a i a_i ai 从大到小排序。然后贪心求时间,具体为分别求在 A A A 机器上和 B B B 机器上至少要耗费多少时间,对于 A A A,先把时间全部加上先在 A A A 上工作的机器所耗费的时间,在计算在 B B B 上工作完后在 A A A 上工作的机器所耗费的时间。但此时发现还是会有问题,因为贪心策略会有问题,加上2000遍随机化就行了。记得及时排除肯定错误的答案。
struct node{
int t,a,b;
bool operator < (const node &x) const{
if(t==1) return b==x.b?a<x.a:b>x.b;
if(t==2) return a==x.a?b<x.b:a>x.a;
}
}r[N],r1[N],r2[N];
int gettime(){
int ta=0,tb=0,Ta,Tb;
for(int i=1;i<=t2;i++) tb+=r2[i].b;
for(int i=1;i<=t1;i++){
ta+=r1[i].a;
if(ta<=tb) tb+=r1[i].b;
else tb=ta+r1[i].b;
}
Ta=tb;ta=tb=0;
for(int i=1;i<=t1;i++) ta+=r1[i].a;
for(int i=1;i<=t2;i++){
tb+=r2[i].b;
if(tb<=ta) ta+=r2[i].a;
else ta=tb+r2[i].a;
}
Tb=ta;
return max(Ta,Tb);
}
int solve(){
t1=t2=0;
for(int i=1;i<=n;i++) r[i].t==1?r1[++t1]=r[i]:r2[++t2]=r[i];
sort(r1+1,r1+t1+1);sort(r2+1,r2+t2+1);
int res=gettime(),T=1000,sa,sb,ta,tb,tmp;
while(T--){
if(t1) sa=rand()%t1+1,ta=rand()%t1+1;
if(t2) sb=rand()%t2+1,tb=rand()%t2+1;
if(t1) swap(r1[sa],r1[ta]);
if(t2) swap(r2[sb],r2[tb]);
tmp=gettime();
if(tmp<res) res=tmp;
else{//排除错误答案
if(t1) swap(r1[sa],r1[ta]);
if(t2) swap(r2[sb],r2[tb]);
}
}
return res;
}
int main(){
read(n);
for(int i=1;i<=n;i++){
read(r[i].t),read(r[i].a),read(r[i].b);
if(r[i].t==3) s[++tot]=i;
}
for(int i=0;i<(1<<tot);i++){
for(int j=0;j<tot;j++) r[s[j+1]].t=((i>>j)&1)+1;//此处深搜也可以
ans=min(ans,solve());
}
prt(ans);
return 0;
}
赛车游戏
油耗的式子 f = a v + b s f=av+bs f=av+bs是不是跟一次函数很像,那整体耗油就转化为 f = a × v + ∑ i = 1 n b × k i f=a\times v +\sum_{i=1}^{n}b\times k_i f=a×v+∑i=1nb×ki,根据题意,在不考虑下坡的特殊性的情况下,速度越大,时间越少,油耗越接近 f f f 但此题下坡可能不耗油,所以这些油耗就可以分给其他坡,而我们仍然可以认为将这些“负下坡”的速度视为 v v v ,最后二分整体速度就行了。j计算时间时注意"负下坡"的速度取到油耗为0的值。当 a v + b k ≤ 0 av+bk\leq0 av+bk≤0时,速度还可以往上提到临界 m i n ( v m a x , − b k / a ) min(vmax,−bk/a) min(vmax,−bk/a).
#include<bits/stdc++.h>
#define N 20001
using namespace std;
double eps=1e-15;
double a,b,y[N],x[N],f,vmax,k[N],l[N];
int n,T;
int fcmp(double x) {
if (x>eps) return 1;
if (x<-eps) return -1;
return 0;
}
bool check(double mid) {
int i;
double sum=0;
for (i=1; i<=n; i++) {
sum+=max(0.0,mid*a+k[i]*b)*l[i];
if (fcmp(sum-f)>0) return 0;
}
if (fcmp(sum-f)<=0) return 1;
return 0;
}
double cal(double mid) {
int i;
double tim=0;
if (mid<=eps) return 0;
for (i=1; i<=n; i++) {
double tmp=a*mid+b*k[i];
if (tmp<=eps) {
double v=min(vmax,-b*k[i]/a);
tim+=l[i]/v;
} else
tim+=l[i]/mid;
}
return tim;
}
int main() {
int i;
cin>>T;
while (T--) {
scanf("%lf%lf%lf%lf",&a,&b,&vmax,&f);
scanf("%d",&n);
for (i=1; i<=n; i++) {
scanf("%lf%lf",&x[i],&y[i]);
x[i]/=1000.0;
y[i]/=1000.0;
k[i]=y[i]/x[i];
l[i]=sqrt(y[i]*y[i]+x[i]*x[i]);
}
double l=0,r=vmax,as=0,t=0;
while (t<=50) {
t++;
double mid=(l+r)/2.0;
if (check(mid)) l=mid;
else r=mid;
}
as=cal(l);
if (as<=eps) printf("IMPOSSIBLE\n");
else printf("%.5lf\n",as);
}
}