Data-2021-01-03_zhr

[CERC2016]Easy Equation

构造一组初始解 \((k,1,0)\),然后分析那个式子,得到了由一个解转移到另一个解的式子:\((a,b,c)\to(k\cdot (b+c),b,c)\)

于是就可以做了。

luogu 上的原 SPJ 把我一开始的错误代码判过了,于是我发了个贴,现在管理员已经修掉了。

我当时还以为我写的高精复杂度怒踩 EI,实际上人用 Hash 判重不可能比我这个 log 要慢。

const int M=1e4;
int k,n;
int ps[4]={0,10,100,1000};
struct gyq
{
    int s[33];
    inline void mem0(){memset(s,0,sizeof(s));return;}
    inline void updata(){for(;s[s[0]+1];s[0]++,s[s[0]]%=M)s[s[0]+2]+=s[s[0]+1]/M;return;}
    inline void up(){for(int i=1;i<=s[0];i++)s[i+1]+=s[i]/M,s[i]%=M;updata();return;}
    inline void downdata(){for(;s[0]&&!s[s[0]];s[0]--);return;}
    inline void down(){for(int i=1;i<=s[0];i++)if(s[i]<0)s[i]+=M,s[i+1]--;downdata();return;}
    inline void pri(){if(!s[0])putchar(48);for(int i=s[0];i>=1;i--){if(i!=s[0])for(int j=0;j<4;j++)if(s[i]<ps[j])putchar(48);printf("%d",s[i]);}return;}
};
inline gyq operator +(gyq x,gyq y){gyq z=x;z.s[0]=max(z.s[0],y.s[0]);for(int i=1;i<=y.s[0];i++)z.s[i]+=y.s[i];z.up();return z;}
inline gyq operator -(gyq x,gyq y){gyq z=x;z.s[0]=max(z.s[0],y.s[0]);for(int i=1;i<=y.s[0];i++)z.s[i]-=y.s[i];z.down();return z;}
inline gyq operator *(gyq x,int y){gyq z=x;for(int i=1;i<=z.s[0];i++)z.s[i]*=y;z.up();return z;}
inline bool operator >(gyq x,gyq y){if(x.s[0]!=y.s[0])return x.s[0]>y.s[0];for(int i=x.s[0];i>=1;i--){if(x.s[i]>y.s[i])return true;if(x.s[i]<y.s[i])return false;}return false;}
inline bool operator >=(gyq x,gyq y){if(x.s[0]!=y.s[0])return x.s[0]>y.s[0];for(int i=x.s[0];i>=1;i--){if(x.s[i]>y.s[i])return true;if(x.s[i]<y.s[i])return false;}return true;}
inline bool operator <(gyq x,gyq y){if(x.s[0]!=y.s[0])return x.s[0]<y.s[0];for(int i=x.s[0];i>=1;i--){if(x.s[i]<y.s[i])return true;if(x.s[i]>y.s[i])return false;}return false;}
inline bool operator <=(gyq x,gyq y){if(x.s[0]!=y.s[0])return x.s[0]<y.s[0];for(int i=x.s[0];i>=1;i--){if(x.s[i]<y.s[i])return true;if(x.s[i]>y.s[i])return false;}return true;}
inline bool operator ==(gyq x,gyq y){for(int i=0;i<=x.s[0];i++)if(x.s[i]!=y.s[i])return false;return true;}
inline void jh(gyq x,gyq y){gyq z=x;x=y;y=z;return;}

map<gyq,bool>g;

struct zjj
{
    gyq a,b,c;
    inline void mem0(){a.mem0();b.mem0();c.mem0();return;}
    inline void init(){mem0();a.s[0]=b.s[0]=b.s[1]=1;a.s[1]=k;return;}
    inline void push(gyq x,gyq y,gyq z){a=x;b=y;c=z;if(a<b)jh(a,b);if(a<c)jh(a,c);if(b<c)jh(b,c);return;}
    inline void pri(){a.pri();putchar(' ');b.pri();putchar(' ');c.pri();putchar('\n');return;}
    inline bool check(){if(g[a]||g[b]||g[c]||a==b||b==c||a==c)return false;g[a]=true;g[b]=true;g[c]=true;return true;}
};
queue<zjj>d;
inline zjj start_zjj(){zjj st;st.init();return st;}
inline bool operator >(zjj x,zjj y){return (x.a==y.a)?((x.b==y.b)?(x.c>y.c):(x.b>y.b)):(x.a>y.a);}
inline bool operator <(zjj x,zjj y){return (x.a==y.a)?((x.b==y.b)?(x.c<y.c):(x.b<y.b)):(x.a<y.a);}

inline void add(gyq a,gyq b,gyq c)
{
    gyq z=(b+c)*k;if(a>=z)return;
    zjj ad;ad.push(z-a,b,c);
    d.push(ad);
    return;
}

int cutt;
inline void work()
{
    zjj now=d.front();d.pop();
    if(now.a.s[0]<=25&&now.c.s[0]&&now.check())now.pri(),cutt--;
    add(now.a,now.b,now.c);
    add(now.b,now.a,now.c);
    add(now.c,now.a,now.b);
    return;
}
int main()
{
    gyq Alpha,Beta;
    int i,j;
    k=rin();n=rin();
    d.push(start_zjj());
    for(cutt=n;cutt;)work();
    return 0;
}

[CERC2017]Buffalo Barricades

单独写了一文


[ICPC2019 WF]Directing Rainfall

首先考虑排序,类似扫描线的过程,从左到右扫过去,每次加点的时候,连两条有向边:

  • 下方第一条直线向自己连边。

  • 向上方第一条直线连边。

连完边以后跑一遍拓扑序,就可以得到处理顺序,因为保证了直线不相交,所以这样是对的。

然后可以用一个数组表示 当前高度下雨水到达某一横坐标的最小代价,把每条直线抽象成两个操作,依次作用:

  • 一个区间的前缀/后缀 取 \(\min\)(扫过去更新)。

  • 区间加 \(1\)

hehezhou:这个东西有 \(100\) 种写法。

这里就采用他比较推荐的第 \(3\) 种写法。

思路是转变为维护差分数组,然后差分的 区间 前缀/后缀 取 \(\min\) 是可以通过将所有 正值 向正方向(取 \(\min\) 方向)移动,一直到遇到某个负值将其值抵消或者到达区间边界以后直接丢出去。

然后区间加则可以修改两个位置的值,一个加,一个减。

这是可以均摊分析掉的,因为每次区间加操作产生的 负值 位置的势能是 \(\operatorname O(1)\) 的。

于是维护两个 set 即可。

posted @ 2021-01-04 18:15  zjjws  阅读(148)  评论(0编辑  收藏  举报