NOI Day1線上同步賽夢遊記

Preface

第一次體驗NOI,雖然不是正式選手,可是打打同步賽仍是挺漲姿式的,也算是體驗了一把。git

Day1很爆炸,一方面是NOI題目的難度高於自身的水平,另外一方面也出現了比較大的失誤,T1一個數組沒有清空致使樹的部分分所有爆0了;T3多是蜜汁Hash寫掛(or 題意理解錯誤?)致使暴力(不過話說好多網絡賽的人T3都爆零了)算法

不由想到若是是自身參加NOI雖然不太現實,出現這樣的SB錯誤會不會後悔呢?數組

後面兩題不會,靜候將來填坑。網絡


歸程

整套題目看起來惟一可作的題目(對於我來講),因此讀完題目就立刻看了下部分分post

woc,CCF這麼良心的嗎?因此就來了一波大力分類討論:spa

  • 前30pts(\(1\to 6\)):海拔僅有一種且爲\(1\),這不是良心送分麼?我從終點開始往回刷一遍DJ(別問我爲何不用SPFA),每一次判斷洪水高度是否\(>1\)便可,如果直接輸出最短路徑(反正一條路都沒有了,只能靠腳了),不然輸出\(0\)(直接飆車到達便可)
  • 後面的10pts(\(15\to16\)):注意到數據範圍很小,咱們仍是先預處理。對於每一次詢問,咱們從起點開始BFS,期間只能通過高度大於洪水高度的邊,而後再因此可以到達的點中找出離終點最近的點擊便可。
  • 中間的25pts(\(7\to 11\)):保證數據爲鏈or樹,因爲樹的特殊性,咱們直接考慮樹怎麼作。首先最短路是要跑的(直接用DJ也行,不過本身再寫一個BFS什麼的會更快),而後咱們直接以終點爲根,預處理出LCA數組(用來跳father)以及倍增數組(記錄每一段樹鏈上權值最小的邊)。考慮洪水淹沒的過程,只要一條邊斷了,這條路就不得不中止。因此咱們樹上倍增找到第一條被淹沒的邊,而後在輸出這個點到根的路徑長度便可(用前面的兩個數組作相似於倍增LCA的過程)。
  • 中間的15pts(\(13\to 14\)):這個咱們注意到離線這個重要性質。咱們把因此操做讀進來,按洪水高度從大到小排個序。這時候咱們發現對於操做的進行其實就是個不斷地給原圖加邊的一個過程,因此咱們用並查集維護,順便維護每個聯通塊內的點距離終點最近的距離便可。

而後滿分算法蒟蒻就不會了,其實按照上面的離線方法是能夠跑在線的狀況的,只不過須要可持久化並查集。而後因爲我很菜,一直都沒調出來,因此仍是等着之後再填吧。code

具體同步賽的時候因爲樹的狀況倍增數組沒有清空,因此少得了15pts(還好我把鏈和暴力一塊兒作了),而且最後離線想到了懶得寫了(在剛T2,T3)排序

這裏上分類討論CODE(能過前16組數據,因此在Luogu上這一題顯示過了,好像還很快ip

#include<cstdio>
#include<cctype>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int N=2e5+5,M=4e5+5,MINER_N=1505,P=21;
struct edge
{
    int to,next,v,h;
}e[M<<1];
struct data
{
    int num,s;
    bool operator <(const data a) const { return a.s<s; }
};
struct double_edge
{
    int l,r,s;
}a[M];
struct ques
{
    int x,h,id;
}b[N>>1];
priority_queue<data> small;
int n,m,q,k,s,ans,x,y,l,h,cnt,head[N],dis[N],t,que[MINER_N],f[N][P],father[N][P],sum[N],fa[N],Ans[N>>1];
bool flag,vis[N],get[MINER_N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void write(int x)
{
    if (x>9) write(x/10);
    putchar(x%10+'0');
}
inline void double_add(int x,int y,int v,int h)
{
    e[++cnt].to=y; e[cnt].next=head[x]; e[cnt].v=v; e[cnt].h=h; head[x]=cnt;
    e[++cnt].to=x; e[cnt].next=head[y]; e[cnt].v=v; e[cnt].h=h; head[y]=cnt;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline void clear(void)
{
    memset(head,-1,sizeof(head)); cnt=ans=0; flag=1;
    memset(f,0,sizeof(f)); memset(sum,0,sizeof(sum));
}
inline void Dijkstra(int s)
{
    memset(dis,63,sizeof(dis)); memset(vis,0,sizeof(vis));
    small.push((data){s,0}); dis[s]=0;
    while (!small.empty())
    {
        int now=small.top().num; small.pop();
        if (vis[now]) continue; vis[now]=1;
        for (register int i=head[now];~i;i=e[i].next)
        if (dis[e[i].to]>dis[now]+e[i].v)
        {
            dis[e[i].to]=dis[now]+e[i].v;
            small.push((data){e[i].to,dis[e[i].to]});
        }
    }
}
inline void reset(int now)
{
    for (register int i=0;i<P-1;++i)
    if (father[now][i]) father[now][i+1]=father[father[now][i]][i],f[now][i+1]=min(f[now][i],f[father[now][i]][i]);
}
inline void DFS(int now,int fa)
{
    register int i; father[now][0]=fa; reset(now);
    for (i=head[now];~i;i=e[i].next)
    if (e[i].to!=fa) f[e[i].to][0]=e[i].h,sum[e[i].to]=sum[now]+e[i].v,DFS(e[i].to,now);
}
inline void solve1(int x,int h)
{
    write(ans=(h>=1?dis[x]:0)); putchar('\n');
}
inline void solve2(int x,int h)
{
    for (register int i=P-1;i>=0;--i)
    if (father[x][i]&&f[x][i]>h) x=father[x][i];
    write(ans=sum[x]); putchar('\n');
}
inline void solve3(int x,int h)
{
    register int i,H=0,T=1; memset(get,0,sizeof(get));
    que[1]=x; get[x]=1; int res=dis[x];
    while (H<T)
    {
        int now=que[++H];
        for (i=head[now];~i;i=e[i].next)
        if (e[i].h>h&&!get[e[i].to]) res=min(res,dis[e[i].to]),get[e[i].to]=1,que[++T]=e[i].to;
    }
    write(ans=res); putchar('\n');
}
inline bool cmp1(double_edge a,double_edge b)
{
    return a.s>b.s;
}
inline bool cmp2(ques a,ques b)
{
    return a.h>b.h;
}
inline int getfa(int k)
{
    return k^fa[k]?fa[k]=getfa(fa[k]):k;
}
inline void unionn(int x,int y)
{
    int fx=getfa(x),fy=getfa(y);
    if (fx!=fy) 
    {
        if (dis[fx]<dis[fy]) fa[fy]=fx; else fa[fx]=fy;
    }
}
inline void solve4(void)
{
    register int i,p=1; 
    for (i=1;i<=q;++i)
    read(b[i].x),read(b[i].h),b[i].id=i;
    sort(a+1,a+m+1,cmp1); sort(b+1,b+q+1,cmp2);
    for (i=1;i<=n;++i) fa[i]=i;
    for (i=1;i<=q;++i)
    {
        while (p<m&&a[p].s>b[i].h) unionn(a[p].l,a[p].r),++p;
        Ans[b[i].id]=dis[getfa(b[i].x)];
    }
    for (i=1;i<=q;++i)
    write(Ans[i]),putchar('\n');
}
int main()
{
    //freopen("return.in","r",stdin); freopen("return.out","w",stdout);
    register int i; read(t);
    while (t--)
    {
        read(n); read(m); clear();
        for (i=1;i<=m;++i)
        {
            read(x); read(y); read(l); read(h); a[i]=(double_edge){x,y,h};
            double_add(x,y,l,h); if (h^1) flag=0;
        }
        if (m!=n-1) Dijkstra(1); else DFS(1,0); read(q); read(k); read(s);
        if (!k&&q==100000&&!flag&&m!=n-1) { solve4(); continue; }
        while (q--)
        {
            read(x); read(y); x=(1LL*x+k*ans-1)%n+1; y=(1LL*y+k*ans)%(s+1);
            if (flag) { solve1(x,y); continue; }
            if (m==n-1) solve2(x,y); else solve3(x,y);
        }
    }
    return 0;
}

冒泡排序

那個板子其實剛開始是錯的並且同步賽好像並無更正字符串

蒟蒻表示這種數學向的題目太恐怖,因此一直企圖切出前\(11\)個點的狀壓DP

然而什麼也沒搞出來,最後交了個全排列證實我曾經來過水了8分


你的名字

字符串什麼也不會好吧Hash的奇技淫巧我仍是挺熟練的的菜雞表示看到題目直接棄療。

直奔12pts的Hash暴力而去,最後爆\(0\)了,我應該是看錯題意了。

反正一堆dalao說着線段樹套SAM過,我仍是回去老老實實學ACWA自動機吧


Postscript

反正我太菜了也沒什麼關係只要不是真的NOI爆炸我仍是能夠接受的

爭取明年同步賽不要翻低級錯誤不敢說出去NOI的我

相關文章
相關標籤/搜索