冲刺国赛模拟 9

既然 2 号那就说明明天可以摆一天了。开一下挂了好久没学的一些东西,先不做 AGC 了。

喜报:我多项式没出门!

哈密顿路

首先哈密顿路有个经典状压 dp:\(dp_{i,S}\) 为以 \(i\) 结尾能否经过 \(S\) 所有点。然后一个经典优化是把 \(i\) 这一维拿 int 压起来,复杂度 \(O(n2^n)\)。然后找答案就枚举一个端点和在它那一边的集合 \(S\) 就能算了。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <iostream>
using namespace std;
int n,dp[1<<24],a[25];
char s[30];
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%s",s);
        for(int j=0;j<n;j++)a[i]|=(s[j]-'0')<<j;
    }
    dp[1]=1;
    for(int s=1;s<(1<<n);s++){
        for(int i=0;i<n;i++){
            if((s>>i)&1)continue;
            if(dp[s]&a[i])dp[s|(1<<i)]|=1<<i;
        }
    }
    const int U=(1<<n)-1;
    for(int i=0;i<n;i++){
        int ans=0;
        for(int s=1;s<(1<<n);s++){
            if((dp[s]>>i)&1)ans|=dp[(U^s)|1];
        }
        for(int j=0;j<n;j++)putchar(((ans>>j)&1)+'0');puts("");
    }
    return 0;
}

统一省选

赛时玩了一小时克克莉丝小姐 T3 导致没写完,交了一发获得 30 分。

答案是分段函数 \(f(H)=\begin{cases}\text{illegal},&H<x\\a,&x\le H<y\\H+b,&H\ge y\end{cases}\),那随手合并,线段树二分一下。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#define int long long
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,H;
pair<int,int>a[1000010];
struct node{
    int x,y,a,b;
    int calc(int val){
        if(val<x)return 0;
        else if(val<y)return b;
        else return val+a;
    }
    node operator+(node s){
        node ans;
        if(max(x,s.x)<inf&&(y<inf||b>=s.x)){
            ans.x=x;
            if(b<s.x)ans.x=max(ans.x,s.x-a);
        }
        else ans.x=inf;
        if(max(y,s.y)<inf){
            ans.y=y;
            if(b<s.y)ans.y=max(ans.y,s.y-a);
            ans.a=a+s.a;
        }
        else ans.y=inf,ans.a=0;
        ans.b=inf;
        if(ans.x<ans.y){
            if(b<s.x)ans.b=s.b;
            else ans.b=s.calc(b);
        }
        else ans.b=0;
        return ans;
    }
}tree[4000010];
void pushup(int rt){
    tree[rt]=tree[lson]+tree[rson];
}
void build(int rt,int L,int R){
    if(L==R){
        if(a[L].first==1)tree[rt]={a[L].second+1,a[L].second+1,-a[L].second,1};
        if(a[L].first==2)tree[rt]={a[L].second,inf,0,a[L].second};
        if(a[L].first==3)tree[rt]={1,a[L].second,0,a[L].second};
        return;
    }
    int mid=(L+R)>>1;
    build(lson,L,mid);build(rson,mid+1,R);
    pushup(rt);
}
void update(int rt,int L,int R,int pos){
    if(L==R){
        if(a[L].first==1)tree[rt]={a[L].second+1,a[L].second+1,-a[L].second,1};
        if(a[L].first==2)tree[rt]={a[L].second,inf,0,a[L].second};
        if(a[L].first==3)tree[rt]={1,a[L].second,0,a[L].second};
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)update(lson,L,mid,pos);
    else update(rson,mid+1,R,pos);
    pushup(rt);
}
pair<node,int> query(int rt,int L,int R,int l,node pre){
    int mid=(L+R)>>1;
    if(l>mid)return query(rson,mid+1,R,l,pre);
    if(L==R){
        node ret=pre+tree[rt];
        if(ret.calc(H)<=0)return make_pair(pre,R-1);
        else return make_pair(ret,R);
    }
    if(l<=L){
        node ret=pre+tree[lson];
        if(ret.calc(H)<=0)return query(lson,L,mid,l,pre);
        else return query(rson,mid+1,R,l,ret);
    }
    pair<node,int> tmp=query(lson,L,mid,l,pre);
    if(tmp.second<mid)return tmp;
    return query(rson,mid+1,R,l,pre+tmp.first);
}
signed main(){
    scanf("%lld%lld%lld",&n,&m,&H);
    for(int i=1;i<=n;i++){
        int od,x;scanf("%lld%lld",&od,&x);
        a[i]=make_pair(od,x);
    }
    build(1,1,n);
    while(m--){
        int od;scanf("%lld",&od);
        if(od==1){
            int x,tp,val;scanf("%lld%lld%lld",&x,&tp,&val);
            a[x]=make_pair(tp,val);
            update(1,1,n,x);
        }
        else{
            int x;scanf("%lld",&x);
            pair<node,int>ans=query(1,1,n,x,{1,1,0,1});
            if(ans.second<x)ans.second=-1;
            printf("%lld\n",ans.second);
        }
    }
    return 0;
}

并行计算

他说上界是 \(\max\left(\lceil\log_2n\rceil,\left\lceil\dfrac{2n-2}5\right\rceil\right)\),证明好玄学。

分界是 \(x\le 16\),小于的直接爆搜。大于的看到界是 \(0.4n+O(1)\),那么有 \(6\) 步跳过 \(15\) 个的方法,用这个跳即可,再把边界用前边的处理一下。

几个界是 \(2\)\(4\) 个、\(3\)\(8\) 个、\(4\)\(11\) 个、\(6\)\(16\) 个。

直接硬上是 98 分,要枚举有几个 \(16\),剩下的用 \(11\)然而观察挂的数据剩下的全是 \(6\),因此退一步就行。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
int n,x;
struct node{
    pair<int,int>a[4];
};
vector<node>ans;
void add(int a,int b,int c,int d,int e,int f,int g,int h){
    node ret;
    ret.a[0]=make_pair(x+a,x+b);
    ret.a[1]=make_pair(x+c,x+d);
    ret.a[2]=make_pair(x+e,x+f);
    ret.a[3]=make_pair(x+g,x+h);
    ans.push_back(ret);
}
void solve16(){
    add(1,2,4,5,7,8,10,11);
    add(2,3,5,6,8,9,11,12);
    add(3,4,3,5,3,6,13,14);
    add(6,7,6,8,6,9,14,15);
    add(9,10,9,11,9,12,15,16);
    add(12,13,12,14,12,15,12,16);
}
void solve11(){
    add(1,2,3,4,5,6,8,9);
    add(2,3,2,4,6,7,9,10);
    add(4,5,4,6,4,7,10,11);
    add(7,8,7,9,7,10,7,11);
}
void solve8(){
    add(1,2,3,4,5,6,7,8);
    add(2,3,2,4,6,7,6,8);
    add(4,5,4,6,4,7,4,8);
}
void solve4(){
    add(1,2,3,4,1145,1145,1145,1145);
    add(2,3,2,4,1145,1145,1145,1145);
}
void cond(){
    int cnt=n-x;
    if(cnt==6){
        for(int i=0;i<6;i++)ans.pop_back();
        x-=15;
        solve11();x+=10;solve11();
    }
    else if(cnt<=1)return;
    else if(cnt<=4)solve4();
    else if(cnt<=8)solve8();
    else if(cnt<=11)solve11();
    else if(cnt<=16)solve16();
}
int main(){
    scanf("%d",&n);
    for(x=0;x+15<n;x+=15)solve16();
    cond();
    printf("%d\n",(int)ans.size());
    for(node x:ans){
        for(int i=0;i<4;i++)printf("%d %d %d ",x.a[i].second,x.a[i].first,x.a[i].second);
        puts("");
    }
    return 0;
}
posted @ 2023-05-31 16:54  gtm1514  阅读(12)  评论(0编辑  收藏  举报