8.31

今天就一个下午是写题,是模拟赛

Password

  首先你可以明显观察出来是要求 \(border\) 的。
  怎么查在中间出现过呢?
  首先所有答案的可能性就是不停跳 \(border\)
  然后出现$1-n-1 $出现过的border长度的最大值就是答案。
  自己画一下应该就知道这个的正确性显然吧。  

New Year Snowmen

  这个题目就是离散化一下然后记录各个数的出现次数。
  重载一下你的优先队列,是出现次数最多的在堆顶。
  每次弹出的三个数肯定是不同的,然后排下就是一次的答案。
  对于还有剩的数塞回去就行了。
  复杂度有保证。

Yaroslav and Divisors

  
  T4,但是T3。
  明显可以扫描线,你所要做的是求出加入一个新的点后删除前面的点的价值会增加多少。
  因为是排列,直接暴力把点全部枚举出来然后加为边,遍历到的时候把边的v中比自己小的点上加上一点价值(只往小的加是为了去重和保证正确性)。
  就这样结了。
  小粘一段代码:


F(i,1,n)
    for(int j=1;j*a[i]<=n;++j)if(loc[j*a[i]]){
        e[loc[j*a[i]]].push_back(i);
        e[i].push_back(loc[j*a[i]]);
    }
void add(int x){
    bit.add(x,1);
    for(int v:e[x])if(v<x)bit.add(v,1);
}
void solve(){
    int pos=0;
    F(i,1,m){
        while(pos<qe[i].r)add(++pos);
        ans[qe[i].id]=bit.sum(qe[i].r)-bit.sum(qe[i].l-1);
    }
}

Password

  为什么还是 \(Password\) ?
  这题还是有很多技巧可以学的.
  首先,题目要求的是\(k\)个开启,然后我们修改的一个区间,那么我们可以异或差分一下:

for(int i=n+1;i>=1;i--)a[i]^=a[i-1]; 

为什么是倒着做?类比一下正常的差分.

然后你发现这里面会出现很多0,但是只有最多\(2*k\)个1,我们只在乎\(1\),所以说我们可以对各个需要变成1的位置来状压,就像离散化一样.
注意到这里的异或差分还带来了一个很好的性质:
你对 \(a[i]--a[i+j+1]\) 的位置异或一下,其实就是把\(a[i]和a[i+j+2]\)这两个位置取反一下.
我们更形象的理解一下,把一个\(i上的1\) 异或 \(len\) 长的区间,实际上就是把这个 \(1\) 右/左移了 \(len\) 长,两个\(1\)会撞在一起变为\(0\).

1 1 1 0 1 1 1 1 1 0
1 0 0 1 1 0 0 0 0 1 0

我们要做的是然我们想要\(1\)的所有位置变为\(1\),其他的位置为\(0\).
这下就是状压\(Dp\)了.
但是还有一个位置,就是转移怎么算.
我们发现这是一个最短路问题.
直接粘代码就知道这个你要算的是什么了:

void bfs(int s){
    memset(dis,127,sizeof(dis));
    queue<int>q;q.push(s);
    inf=dis[0];dis[s]=0;
    while(!q.empty()){
        int u=q.front();q.pop();
        F(i,1,l){
            int x=u+e[i];
            if(x<=n+1&&dis[x]==inf){
                dis[x]=dis[u]+1;
                q.push(x);
            }
            x=u-e[i];
            if(x>=1&&dis[x]==inf){
                dis[x]=dis[u]+1;
                q.push(x);
            }
        }
    }
    F(i,1,n+1)if(bel[i]!=-1)c[bel[s]][bel[i]]=dis[i];
}

最后就是正常的状压背包\(Dp\)了.

posted @ 2023-08-31 19:25  ussumer  阅读(23)  评论(0)    收藏  举报