HNOI2011(部分)

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=1Fv(u,v)E,w(u,v)>>k=0Fv=(u,v)E,w(u,v)>>k=11
最后答案 ∑ k = 1 m a x u 2 k × F 1 \sum_{k=1}^{maxu}2^k\times F_1 k=1maxu2k×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=Fn1×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]=[Fn1n]×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]=[Fn1n1]×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;
}

数矩形

做这题的时候,一开始认为很简单,以为矩形只有横着的,结果样例都过不了,后来发现斜着也有矩形 ,这就引出了两个问题:

  1. 面积怎么保证精度
  2. 怎么确定四点是否为一个矩形
    对于第一个,因为题上说输出一个非负整数,于是我就很自然的想到为什么答案一定是一个整数?想了很久最后还是推出来了,如下图。
    请添加图片描述
    这个推出来后,顺便也解决了精度的问题,就不用长乘宽,保证了答案正确。
    第二个,就可以根据矩形的性质:对角线相等,对角线的中点坐标都是同一个。
    于是我们可以先枚举出所有对角线,把每条对角线的长度(存平方),中点坐标(不除二),两个端点(为了求面积)。然后排完序后,可以组成矩形的对角线就自然而然挨在一起了。
#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+bk0时,速度还可以往上提到临界 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值