July 22, 2019 (Data Structure and Search)

Posted by cbassett03 on Wed, 07 Aug 2019 02:41:01 +0200

The test was fried!!!!!

What a ghost, xie. I'm afraid the noip will explode this year.

I haven't finished the exam yet. Let's make a summary. I really don't want to say anything in the exam today...

Write your questions carefully...

Arrange the questions according to their difficulty level first. Only problem2 is written in the exam. If there is something wrong with it, write the solution to the question first.

prob2: set

Main idea: There is a tree where each node corresponds to a set. It starts with an empty set, performs some operations (adding or deleting elements) on each node from root to leaf, and finally outputs the size of the set corresponding to each node.

My practice: Tree operation, the order is from root to leaf, the first thought is \(dfs\) wave.Then for the operation of removing and deleting elements from a set, it is conceived that a range segment tree should be built, and then the operation of this node should be performed in the order of dfs. After traversing a subtree where this node is the root, the operation of this node will be restored, so that the output of the answers of other subtrees will not be affected.

It looks like the big guys have a way to do it with only \(dfs\), but the ideas are similar. If several other questions are blown up and need to be sorted out, there is no way to look at his method.

Okay, I didn't expect this to fly \(RE\).Think carefully, it should have been thought that even though the weight segment tree, the number of nodes should be open\(4\) times, the number of times!!!!!

ririririri......

Sweat, this really took the test,

The code is as follows (changed):

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define xx 110000
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct line_tree{int l,r,sz,ans;}lt[xx<<2];
int num=0,res[xx],now;
vector<int>e[xx],ee[xx],eee[xx];
bool vis[xx];
inline void build(int k,int i,int j)
{
    lt[k].l=i;lt[k].r=j;
    if(i==j) return;
    int q=k<<1,mid=(i+j)>>1;
    build(q,i,mid);
    build(q+1,mid+1,j);
}
inline void up(int k)
{
    int q=k<<1;
    lt[k].ans=lt[q].ans+lt[q+1].ans;
}
inline void add(int k,int x,int c)
{
    if(lt[k].l==lt[k].r)
    {
        now=lt[k].sz;
        lt[k].sz+=c;
        lt[k].sz=max(lt[k].sz,0);
        lt[k].sz=min(lt[k].sz,1);
        lt[k].ans=lt[k].sz?1:0;
        return;
    }
    int q=k<<1,mid=lt[q].r;
    if(x<=mid) add(q,x,c);
    else add(q+1,x,c);
    up(k);
}
inline void change(int k,int x,int c)
{
    if(lt[k].l==lt[k].r)
    {
        lt[k].sz=c;
        lt[k].ans=lt[k].sz?1:0;
        return;
    }
    int q=k<<1,mid=lt[q].r;
    if(x<=mid) change(q,x,c);
    else change(q+1,x,c);
    up(k);
}
inline void dfs(int g)
{
    fur(i,0,(int)ee[g].size()-1)
    {
        if(ee[g][i]<0) add(1,-ee[g][i],-1);
        else add(1,ee[g][i],1);
        eee[g].push_back(now);
    }
    res[g]=lt[1].ans;
    fur(i,0,(int)e[g].size()-1) if(!vis[e[g][i]]) vis[e[g][i]]=true,dfs(e[g][i]);
    fur(i,0,(int)ee[g].size()-1)
    {
        if(ee[g][i]<0) change(1,-ee[g][i],eee[g][i]);
        else change(1,ee[g][i],eee[g][i]);
    }
}
int main()
{
    int n=in;
    fur(i,1,n-1)
    {
        int x=in,y=in;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    fur(i,1,n)
    {
        int k=in;
        fur(j,1,k)
        {
            int x=in;
            ee[i].push_back(x);
        }
    }
    build(1,1,n);
    vis[1]=true;
    dfs(1);
    fur(i,1,n) printf("%d\n",res[i]);
    return 0;
}

Positive Solution: The idea is the same, but the data structure changes from a range segment tree to a bucket with \(O(1)\).

That's right. Time and space are much more comfortable and there's no nausea. The code is as follows:  
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define xx 110000
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
int num=0,res[xx],before;
vector<int>e[xx],ee[xx],eee[xx];
bool vis[xx],cnt[xx];
inline void dfs(int g)
{
    fur(i,0,(int)ee[g].size()-1)
    {
        before=cnt[abs(ee[g][i])];
        if(ee[g][i]<0) cnt[-ee[g][i]]=false;
        else cnt[ee[g][i]]=true;
        num+=(cnt[abs(ee[g][i])]-before);
        eee[g].push_back(before);
    }
    res[g]=num;
    fur(i,0,(int)e[g].size()-1) if(!vis[e[g][i]]) vis[e[g][i]]=true,dfs(e[g][i]);
    fur(i,0,(int)ee[g].size()-1)
    {
        before=cnt[abs(ee[g][i])];
        if(ee[g][i]<0) cnt[-ee[g][i]]=eee[g][i];
        else cnt[ee[g][i]]=eee[g][i];
        num+=(cnt[abs(ee[g][i])]-before);
    }
}
int main()
{
    int n=in;
    fur(i,1,n-1)
    {
        int x=in,y=in;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    fur(i,1,n)
    {
        int k=in;
        fur(j,1,k)
        {
            int x=in;
            ee[i].push_back(x);
        }
    }
    vis[1]=true;
    dfs(1);
    fur(i,1,n) printf("%d\n",res[i]);
    return 0;
}

prob1: median

Main idea: Give a 1-n ranking and ask the sum of the median m of each interval with odd elements and the product of the left and right ends of the interval

40 points: this part is still given barely enough, less than 1 K is enough to 40 points, which is nothing to say, pure violence, each rearrangement, find the median.Time Complexity\(O(n^3logn)\)

Violence: With the help of the xgzc Big Man, the untouched fantasy of the Capsule has been improved. It is easy to know that there are \(\dfrac{n^2}{2}\) intervals, the median of which is to calculate the interval \mid\ small value in one half wave as a whole, but the Big Man said that the time complexity is \(logO(n^n)).And the constant is big, not good card, so it failed.Using two heaps (one large root and one small root) to maintain the median can achieve the same complexity and constants can be small enough to ignore.Since this topic can be opened \(O2\), and the range of \(n\) is small (\(<=1e4\)), it can be so violent.

Okay, that's okay. You're stuck, the data is big, the seniors are too strong for qwq

The code is as follows:

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define xx 11000
#define ll long long
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
priority_queue<ll>e;
priority_queue<ll,vector<ll>,greater<ll> >f;
ll a[xx],ans=0;
int main()
{
    int n=in;
    fur(i,1,n) a[i]=in;
    fur(i,1,n)
    {
        while(!e.empty()) e.pop();
        while(!f.empty()) f.pop();
        e.push(a[i]);
        ans+=i*i*e.top();
        for(int j=i+2;j<=n;j+=2)
        {
            int now=0;
            fur(k,j-1,j)
            {
                if(!now)
                {
                    if(f.empty()||a[k]<=f.top()) e.push(a[k]);
                    else
                    {
                        e.push(f.top());
                        f.pop();
                        f.push(a[k]);
                    }
                    now++;
                }
                else
                {
                    if(a[k]>=e.top()) f.push(a[k]);
                    else
                    {
                        f.push(e.top());
                        e.pop();
                        e.push(a[k]);
                    }
                }
            }
            ans+=i*j*e.top();
        }
    }
    printf("%lld\n",ans);
    return 0;
}

Positive Solution: By opening a two-way Chain table, the median \(O(1)\ can be transferred.Thoughts are the same as before, not to mention more, but tears.Code above (with the captain's question, can't read, calculate):

#include <iostream>
#include <vector>
using namespace std;
typedef long long int64;
int main() {
    freopen("median.in","r",stdin);
    freopen("median.out","w",stdout);
    int n; cin >> n;
    vector<int> element(n);
    for (int i = 0; i < n; i += 1) {
        cin >> element[i];
    }
    vector<pair<int, int>> neighbours(n + 1);
    int64 result = 0;
    for (int i = 0; i < n; i += 1) {
         //The value is valid if the index is i n the [i, n] range 
        vector<bool> valid(n + 1, false);
        for (int j = i; j < n; j += 1) {
            valid[element[j]] = true;
        }
        valid[n + 1] = true;
        int last = 0;
        //Number of values > Intermediate value, Number of lower values <=Intermediate value
        //Mid->intermediate value
        int bigger = (n - i), lower = 0, mid = 0;
        //  Calculate each value, the next valid value on the left and right 
        for (int j = 1; j <= n; j += 1) {
            if (not valid[j]) {
                continue;
            }
            if (bigger > lower) {
                mid = j;
                bigger -= 1;
                lower += 1;
            }
            // The last element will be the one to the left of the current element
            // The element to the right of the last element will be this element
            neighbours[j].first = last;
            neighbours[last].second = j;
            last = j;
        }
        for (int j = n -1 ; j >= i; j -= 1) {
            if (lower == bigger + 1) {
                // this can be any formula, whatsoever
                result += (int64) (j + 1) * (i + 1) * mid;
            } 
            // Values are on the left and right side of the erased value
            int left = neighbours[element[j]].first;
            int right = neighbours[element[j]].second;
            // Remove this list from the likef list
            neighbours[left].second = right;
            neighbours[right].first = left;
            // Update intermediate, larger, or smaller values.
            if (element[j] == mid) {
                bigger -= 1;
                mid = right;
            } else if (element[j] < mid) {
                lower -= 1;
            } else {
                bigger -= 1;
            }
            while (lower > bigger) {
                mid = neighbours[mid].first;
                bigger += 1;
                lower -= 1;
            }

            // move the median to the right
            while (bigger > lower) {
                mid = neighbours[mid].second;
                bigger -= 1;
                lower += 1;
            }
        }
    }
    cout << result << '\n';
    return 0;
}

Other solutions: Allow me to introduce to you the solution of the \(yyz\) Big Blacks, which is just like the beautiful Moqu Team. It also transfers the \(O(1)\) by deleting and adding value to the \(mid\) position, and achieves the same excellent \(O(n^2)\ algorithm as the correct solution, exploding \(std\).\(orz\)...

See code for details:

#include<iostream>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define int long long
#define xx 10010
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
int cnt[xx],a[xx];
signed main()
{
    int n=in;
    fur(i,1,n) a[i]=in;
    int l=1,r=0,mid=0,cnnt=0,ans=0;
    fur(tl,1,n)
    {
        for(int tr=tl;tr<=n;tr+=2)
        {
            while(l<tl)
            {
                if(a[l]<=mid) --cnnt;
                --cnt[a[l++]];
            }
            while(r<tr)
            {
                ++r;
                if(a[r]<=mid) ++cnnt;
                ++cnt[a[r]];
            }
            while(r>tr)
            {
                if(a[r]<=mid) --cnnt;
                --cnt[a[r--]];
            }
            int need=((tr-tl)>>1)+1;
            if(need==1)
            {
                mid=a[tl];
                cnnt=1;
                ans+=tr*tl*mid;
                continue;
            }
            while(cnnt<need) cnnt+=cnt[++mid];
            while(cnnt>need)
            {
                if(cnnt-cnt[mid]<need) break;
                cnnt-=cnt[mid--];
            }
            while(cnt[mid]==0) --mid;
            ans+=tl*tr*mid;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

prob4: stars

Main idea of the topic: Given some pairs of points on a given plane, each point has its corresponding weight, and the maximum point weight of a given size matrix is obtained.

Scan line bare (unfortunately I don't)

Let's start with 40 points of violence:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
#define xx 55000
inline int read()
{
    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct point{int x,y,z;}dot[xx];
int ans=0;
inline bool cmp(point a,point b){return (a.x^b.x)?(a.x<b.x):(a.y<b.y);}
int main()
{
    int n=in,w=in,h=in;
    fur(i,1,n)
    {
        dot[i].x=in;
        dot[i].y=in;
        dot[i].z=in;
    }
    sort(dot+1,dot+n+1,cmp);
    fur(i,1,n)
    {
        int j=i+1,tmp=dot[i].z;
        while(j<=n&&dot[j].x-dot[i].x<=w)
        {
            if(dot[j].y-dot[i].y<=h&&dot[j].y>=dot[i].y) tmp+=dot[j].z;
            j++;
        }
        int j1=i+1,tmp1=dot[i].z;
        while(j1<=n&&dot[j1].x-dot[i].x<=w)
        {
            if(dot[i].y-dot[j1].y<=h&&dot[j1].y<=dot[i].y) tmp1+=dot[j1].z;
            j1++;
        }
        ans=max(ans,max(tmp,tmp1));
    }
    printf("%d\n",ans);
    return 0;
}

Okay, the water program is going to use the word count, so let's move on to the topic.

Positive solution: Scan line plus segment tree optimization, see code for details:

#include <cstdio>
#include<cstring>
#include <iostream>
#include <algorithm>
#define ls (x<<1)
#define rs (x<<1|1)
using namespace std;
typedef long long ll;
ll geti(){
    char ch=getchar(),k=1;ll ret=0;
    while((ch<'0' || ch>'9') && ch!='0')ch=getchar();
    if(ch=='-')k=0,ch=getchar();
    while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
    return k?ret:-ret;
}
const int maxn = 100000 + 1000;
struct XDS{
    ll mx[maxn*8],lazy[maxn*8];
    void pushdown(int x){
        mx[ls]+=lazy[x];mx[rs]+=lazy[x];
        lazy[ls]+=lazy[x];lazy[rs]+=lazy[x];
        lazy[x]=0;
    }
    void update(int x,int l,int r,int xl,int xr,ll v){
        if(xl==l && r==xr){
            mx[x]+=v;lazy[x]+=v;return;
        }
        int mid=(l+r)>>1;
        if(xr<=mid)update(ls,l,mid,xl,xr,v);
        else if(xl>mid)update(rs,mid+1,r,xl,xr,v);
        else update(ls,l,mid,xl,mid,v),update(rs,mid+1,r,mid+1,xr,v);
        mx[x]=max(mx[ls],mx[rs])+lazy[x];
    }
    ll query(int x,int l,int r,int xl,int xr){
        if(l==xl && r==xr){
            return mx[x];
        }
        int mid=(l+r)>>1;
        if(xr<=mid)return query(ls,l,mid,xl,xr)+lazy[x];
        else if(xl>mid)return query(rs,mid+1,r,xl,xr)+lazy[x];
        else return max(query(ls,l,mid,xl,mid),query(rs,mid+1,r,mid+1,xr))+lazy[x];
    }
}t;
int n,w,h;
struct star{
    ll x,y,ty,l;
}a[maxn];
ll hs[maxn];
bool cmpx(const star &x,const star &y){return x.x<y.x;}
bool cmpy(const star &x,const star &y){return x.ty<y.ty;}
int main()
{
        scanf("%d%d%d",&n,&w,&h)
        for(int i=1;i<=n;i++)
        {
            a[i*2-1].x=geti();a[i*2-1].ty=geti();a[i*2-1].l=geti();
            a[i*2].x=a[i*2-1].x+w+1;a[i*2].ty=a[i*2-1].ty;a[i*2].l=-a[i*2-1].l;
        }
        n=n*2;
        sort(a+1,a+1+n,cmpy);
        int s=0;
        for(int i=1;i<=n;i++)
        {
            if(i!=1 && a[i].ty==a[i-1].ty) a[i].y=s;
            else
            {
                if(a[i].ty-hs[s]>1) ++s,hs[s]=hs[s-1]+1;
                ++s;hs[s]=a[i].ty;
                a[i].y=s;
            }
        }
        hs[++s]=a[n].ty+1;
        sort(a+1,a+1+n,cmpx);
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            int l=a[i].y,r=s,mid,rt=-1;
            ll cur=a[i].ty+h;
            while(l<=r)
            {
                mid=(l+r)>>1;
                if(hs[mid]<=cur) rt=mid,l=mid+1;
                else r=mid-1;
            }
            t.update(1,1,s,a[i].y,rt,a[i].l);
            if(a[i].x!=a[i+1].x || i==n) ans=max(ans,t.query(1,1,s,1,s));
        }
        printf("%lld\n",ans);
        return 0;   
}

prob3: craft

Main idea of the title: Given a matrix of \(c*r\), in which there are two types of grids that can or cannot be passed, given the position of the hero and the two enemies and their blood volume, find out if you can win the game and the minimum number of rounds to win the game

\(MDzhizhang\), the president said that this topic is a way\(sb\) explosive search, you can explosive search questions, do not want to say anything.It doesn't seem like there's much time. The last direct \(kuai\) solution (dp\) used for scaling):

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
LL gi () {
    LL ret=0; char ch=getchar();
    while((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    char c=ch=='-'?getchar():ch;
    while(c>='0' && c<='9') ret=ret*10+c-'0',c=getchar();
    return ch=='-'?-ret:ret;
}
int n,m,tl,pos[37][37],w[37][37],near[37][37],h_m,h_z,dx[4]={0,-1,0,1},dy[4]={-1,0,1,0};
int dp[55][37][37][37][18];
char mp[7][7];
int go (int p,int d) {
    int x = (p-1)/m, y = (p-1)%m;
    int nx = x + dx[d], ny = y + dy[d];
    if(nx < 0 || nx > n || ny < 0 || ny > m || mp[nx][ny]=='1') return -1;
    return pos[nx][ny];
}
int zgo (int p,int g) {
    if(near[p][g] || !p) return p;
    int res = -1, dis = 1e9;
    for(int i=0;i<=3;i+=1) {
        int c = go(p,i);
        if(c==-1) continue;
        if(w[c][g] < dis) dis = w[c][g], res = c;
    }
    if(dis > 100) return p;
    return res;
}
void upd (int &x, int y) {
    x = x < y ? x : y;
}
int main (int argc, char* argv[]) {
    freopen("craft.in","r",stdin);
    freopen("craft.out","w",stdout);
    n = gi(), m = gi(), tl = gi();
    for(int i=0;i<n;i+=1) scanf("%s",mp[i]);
    h_m = gi(); h_z = gi();
    int p1 = -1, p2 = -1, p3 = -1;
    for(int i=0;i<n;i+=1) {
        for(int j=0;j<m;j+=1) {
            pos[i][j] = i*m+j+1;
            if(mp[i][j]=='z' || mp[i][j]=='Z') {
                if(p2 == -1) p2 = pos[i][j]; else p3 = pos[i][j];
            }
            else if(mp[i][j]=='M') p1 = pos[i][j];
        }
    }
    memset(w,60,sizeof w);
    for(int i=0;i<n;i+=1) {
        for(int j=0;j<m;j+=1) {
            if(mp[i][j] == '1') continue;
            if(i && mp[i-1][j] != '1') w[pos[i][j]][pos[i-1][j]] = 1;
            if(i!=n-1 && mp[i+1][j] != '1') w[pos[i][j]][pos[i+1][j]] = 1;
            if(j && mp[i][j-1] != '1') w[pos[i][j]][pos[i][j-1]] = 1;
            if(j!=m-1 && mp[i][j+1] != '1') w[pos[i][j]][pos[i][j+1]] = 1;
            w[pos[i][j]][pos[i][j]] = 0;
        }
    }
    int sz = n*m;
    for(int i=1;i<=sz;i+=1) {
        for(int d=0;d<=3;d+=1)
            if(go(i,d) != -1) near[i][go(i,d)] = 1;
    }
    for(int k=1;k<=sz;k+=1)
        for(int i=1;i<=sz;i+=1)
            for(int j=1;j<=sz;j+=1) w[i][j] = min(w[i][j], w[i][k] + w[k][j]);
    memset(dp,60,sizeof dp);
    dp[0][p1][p2][p3][h_m] = h_z * 2;
    dp[0][p1][p3][p2][h_m] = h_z * 2;
    for(int i=0;i<tl;i+=1)
        for(int t1=1;t1<=sz;t1+=1)
            for(int t2=0;t2<=sz;t2+=1)
                for(int t3=0;t3<=sz;t3+=1)
                    for(int h=1;h<=h_m;h+=1) {
                        int cur = dp[i][t1][t2][t3][h];
                        if(cur > 100) continue;
                        // 1. move
                        for(int d=0;d<=3;d+=1) {
                            int n1 = go(t1,d);
                            if(n1 == -1 || n1 == t2 || n1 == t3) continue;
                            int v = 0;
                            int n2 = zgo(t2,n1), n3 = zgo(t3,n1);
                            if(near[t2][n1]) ++v;
                            if(near[t3][n1] && t2 != t3 && cur > h_z) ++v;
                            if(h-v > 0) {
                                upd(dp[i+1][n1][n2][n3][h-v], cur);
                            }
                        }
                        // 2. attack
                        int n2 = zgo(t2,t1), n3 = zgo(t3,t1);
                        if(cur == 1) {
                            printf("WIN\n%d",i+1); exit(0);
                        }
                        int v = 0;
                        if(near[t2][t1]) ++v;
                        if(near[t3][t1] && t2 != t3 && cur-1 > h_z) ++v;
                        if(h-v > 0) {
                            if(cur-1 > h_z) {
                                upd(dp[i+1][t1][n2][n3][h-v], cur-1);
                            }
                            else {
                                upd(dp[i+1][t1][n2][0][h-v], cur-1);
                            }
                        }
                    }
    puts("LOSE");
    return 0;
}

Hope tomorrow's chart will tell you a different story

Topics: PHP less