Loading

Noip模拟17 2021.7.16

我愿称这场考试为STL专练

T1 世界线

巧妙使用$bitset$当作vis数组使用,内存不会炸,操作还方便,的确是极好的。

但是这个题如果不开一半的$bitset$是会炸内存的,因为他能开得很大,但是也有很大代价

相比bool是好的,所以这题要用一个节点池来存放节点卡内存限制

但是当时考试的时候还不知道有这种东西。。

考试时候的想法是:

因为发现如果暴力的话只用$floyd$循环找一遍无法将所有边找全,于是想到使用多次$floyd$

记一个ans数组记录每一次新产生的连边,如果这一次的ans为0,直接break,然后把ans数组加和输出

因为有大样例,发现跑6次刚刚好

数据范围较小,水五十是可以的

然后说说正解。使用拓扑序思想

因为有多个联通块,则应该找到所有图进行dfs。

使用bitset记录每一个点的子节点可能产生的贡献,将其存到一个进制数中,知道子节点的贡献为0,将其回收

新技巧:

1.内存池回收;

2.$bitset$各种使用方法

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0,f=1; char ch=getchar();
 5     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 6     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
 7     return x*f;
 8 }
 9 const int NN=6e4+5,MM=1e5+5;
10 int n,m,in[NN],ot[NN],tot;
11 struct SNOW{int to,next;}; SNOW e[MM]; int head[NN],rp;
12 inline void add(int x,int y){e[++rp]=(SNOW){y,head[x]}; head[x]=rp;}
13 bitset<60001> s[30001];
14 int id[NN],ans,bin[NN],top;
15 bool vis[NN];
16 inline void calc(int x){
17     ans+=s[id[x]].count()-1-ot[x];
18     s[bin[++top]=id[x]].reset();
19 }
20 inline int New(){
21     if(top) return bin[top--];
22     return ++tot;
23 }
24 inline void dfs(int x){
25     vis[x]=true;
26     if(!id[x]) id[x]=New();
27     s[id[x]].set(x);
28     for(int i=head[x];i;i=e[i].next){
29         int y=e[i].to;
30         if(vis[y]){
31             if(!id[y]) id[y]=New();
32             s[id[x]]|=s[id[y]];
33             in[y]--;
34             if(!in[y]) calc(y);
35             continue;
36         }
37         if(!id[y]) id[y]=New();
38         dfs(y);
39         s[id[x]]|=s[id[y]];
40         in[y]--;
41         if(!in[y]) calc(y);
42     }
43 }
44 namespace WSN{
45     inline int main(){
46         n=read();m=read();
47         for(int i=1;i<=m;i++){
48             int a=read(),b=read();
49             ot[a]++; in[b]++; add(a,b);
50         }
51         for(int i=1;i<=n;i++){
52             if(!vis[i]){
53                 dfs(i);
54                 if(!in[i]) calc(i);
55             }
56         }
57         printf("%d\n",ans);
58         return 0;
59     }
60 }
61 signed main(){return WSN::main();}
View Code

T2 时间机器

不难发现这是个死贪心(然而考场上少判断就贪假了。。)

众所周知,贪心只有0分和100分。。。

不过想要拿到100分还要使用$set$

找到所有满足节点范围左端点限制的电阻,然后将其右端点插入set中,

依次找到符合电压范围的后继右端点电阻,减去个数就行

打的时候好好想着有没有可能假的地方,否则就会假。。。

注意细节

新技巧:

1.$set$快速维护

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int min(int a,int b){return a<b?a:b;}
 4 inline int read(){
 5     int x=0,f=1; char ch=getchar();
 6     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 7     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
 8     return x*f;
 9 }
10 const int NN=5e4+5;
11 int T,n,m;
12 struct nod{
13     int id,r;
14     friend bool operator<(nod a,nod b){return a.r==b.r? a.id<b.id : a.r<b.r;}
15 };set<nod> s;
16 struct node{int r,sum,l;}; node U[NN],R[NN];
17 inline bool cmp(node a,node b){return a.l==b.l?a.r<b.r: a.l<b.l;}
18 namespace WSN{
19     inline int main(){
20         // FILE *A=freopen("1.in","r",stdin);
21         T=read();
22         while(T--){
23             s.clear();
24             n=read();m=read();
25             for(int i=1;i<=n;i++) U[i].l=read(),U[i].r=read(),U[i].sum=read();
26             for(int i=1;i<=m;i++) R[i].l=read(),R[i].r=read(),R[i].sum=read();
27             sort(U+1,U+n+1,cmp); sort(R+1,R+m+1,cmp);
28             nod st; st.r=0x3fffffff,st.id=0; s.insert(st);
29             bool f=0; int j=1;
30             for(int i=1;i<=n;i++){
31                 nod g;
32                 while(R[j].l<=U[i].l&&j<=m)
33                     g.id=j,g.r=R[j].r,j++,s.insert(g);
34                 g.r=U[i].r,g.id=0;//将t设置为电压右端点,找到其后继点
35                 while(U[i].sum){
36                     g=*s.lower_bound(g);
37                     if(g.r==0x3fffffff) break;//无满足电阻,跳出判断
38                     int mn=min(R[g.id].sum,U[i].sum);
39                     U[i].sum-=mn;
40                     R[g.id].sum-=mn; //减去此类满足的电阻个数
41                     if(!R[g.id].sum) s.erase(g);//如果此种类全无,删除此种类电阻
42                 }
43                 if(U[i].sum){ f=1; break;}//如果最后把所有满足电阻都删掉但是节点还有剩余,直接输出
44             }
45             if(f){puts("No");continue;}
46             if(!f){puts("Yes"); continue;}
47         }
48         return 0;
49     }
50 }
51 signed main(){return WSN::main();}
View Code

T3 weight

此题非常牛皮。。

他说最小生成树,就先生成树。

然后将边分为树边和非树边,进行判断。

如果是非树边,他只能最大变成他紧连着的树边里最大的那一个

如果是树边,他顶多变成非树边-1

那么,除了使用数链剖分加线段树维护树边最大值以外,

还要用线段树维护一个最小值(打在一个线段树里,但不用pushup,因为每一段毫无联系(或者说没用),只需下放信息)

查询最小值的话只需要查把边权下放到的那个点即可

在树剖求max时顺便更新最小值

输出的时候还要判断联通

涉及知识点:

1.最小生成树

2.树链剖分+线段树

3.有关边权下放的性质(利用其边权总下放在更深的节点上)

4.并查集判断联通

  1 #include<bits/stdc++.h>
  2 #define lid (id<<1)
  3 #define rid (id<<1|1)
  4 using namespace std;
  5 inline int max(int a,int b){return a>b?a:b;}
  6 inline int min(int a,int b){return a<b?a:b;}
  7 inline void swap(int &x,int &y){x^=y^=x^=y;}
  8 inline int read(){
  9     int x=0,f=1; char ch=getchar();
 10     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 11     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
 12     return x*f;
 13 }
 14 const int NN=700005,MM=1e6+5,inf=0x7fffffff;
 15 int n,m,a,ff[NN],w[NN],ans[MM];
 16 int dfn[NN],rk[NN],son[NN],fa[NN],top[NN],dep[NN],siz[NN],cnt;
 17 bool vis[MM];
 18 struct node{int id,u,v,w;};node mp[NN];
 19 struct SNOW{int from,to,val,next;};SNOW e[MM]; int head[NN],rp;
 20 inline void add(int x,int y,int z){e[++rp]=(SNOW){x,y,z,head[x]}; head[x]=rp;}
 21 inline bool cmp(node a,node b){return a.w<b.w;}
 22 inline int getfa(int x){return ff[x]=((ff[x]==x)?x:getfa(ff[x]));}
 23 inline void kru(){
 24     int seg=0; sort(mp+1,mp+m+1,cmp);
 25     for(int i=1;i<=n;i++) ff[i]=i;
 26     for(int i=1;i<=m;i++){
 27         int x=mp[i].u,y=mp[i].v,ID=mp[i].id;
 28         if(getfa(x)!=getfa(y)){
 29             ff[getfa(x)]=getfa(y);
 30             add(x,y,mp[i].w);
 31             add(y,x,mp[i].w);
 32             seg++; vis[ID]=true;
 33         }if(seg==n-1) break;
 34     }
 35 }
 36 inline void dfs1(int f,int x){
 37     siz[x]=1; fa[x]=f; dep[x]=dep[f]+1;
 38     for(int i=head[x];i;i=e[i].next){
 39         int y=e[i].to;
 40         if(y==f) continue;
 41         w[y]=e[i].val;
 42         dfs1(x,y);
 43         siz[x]+=siz[y];
 44         if(siz[son[x]]<siz[y]) son[x]=y;
 45     }
 46 }
 47 inline void dfs2(int x,int t){
 48     dfn[x]=++cnt; rk[cnt]=x; top[x]=t;
 49     if(!son[x]) return; dfs2(son[x],t);
 50     for(int i=head[x];i;i=e[i].next){
 51         int y=e[i].to;
 52         if(y!=fa[x]&&y!=son[x]) dfs2(y,y);
 53     }
 54 }
 55 struct SNOWtree{
 56     int ll[NN<<2+5],rr[NN<<2+5];
 57     int minn[NN<<2+5],maxn[NN<<2+5],lzy[NN<<2+5];
 58     inline void pushup(int id){
 59         lzy[id]=min(lzy[lid],lzy[rid]);
 60         minn[id]=min(minn[lid],minn[rid]);
 61         maxn[id]=max(maxn[lid],maxn[rid]);
 62     }
 63     inline void pushdown(int id){
 64         if(lzy[id]==inf||ll[id]==rr[id]) return;
 65         minn[lid]=min(minn[lid],lzy[id]);
 66         minn[rid]=min(minn[rid],lzy[id]);
 67         lzy[lid]=min(lzy[lid],lzy[id]);
 68         lzy[rid]=min(lzy[rid],lzy[id]);
 69         lzy[id]=inf;
 70     }
 71     inline void build(int id,int l,int r){
 72         ll[id]=l; rr[id]=r;
 73         if(l==r){
 74             maxn[id]=w[rk[l]];
 75             minn[id]=lzy[id]=inf;
 76             return;
 77         }
 78         int mid=l+r>>1;
 79         build(lid,l,mid); build(rid,mid+1,r);
 80         pushup(id);
 81     }
 82     inline void update(int id,int l,int r,int val){
 83         if(l<=ll[id]&&rr[id]<=r){
 84             minn[id]=min(minn[id],val);
 85             lzy[id]=min(lzy[id],val);
 86             return;
 87         }
 88         pushdown(id);
 89         if(l<=rr[lid]) update(lid,l,r,val);
 90         if(r>=ll[rid]) update(rid,l,r,val);
 91         // minn[id]=min(minn[lid],minn[rid]);
 92     }
 93     inline int query_max(int id,int l,int r){
 94         if(l<=ll[id]&& rr[id]<=r) return maxn[id];
 95         int ans=0;
 96         if(l<=rr[lid]) ans=max(ans,query_max(lid,l,r));
 97         if(r>=ll[rid]) ans=max(ans,query_max(rid,l,r));
 98         return ans;
 99     }
100     inline int query_min(int id,int pos){
101         if(ll[id]==rr[id]) return minn[id];
102         int mid=ll[id]+rr[id]>>1,ans=inf;
103         pushdown(id);
104         if(pos<=mid) ans=min(ans,query_min(lid,pos));
105         else ans=min(ans,query_min(rid,pos));
106         return ans;
107     }
108 }tr;
109 inline int MAX(int x,int y,int val){
110     int ans=0;
111     while(top[x]!=top[y]){
112         if(dep[top[x]]<dep[top[y]]) swap(x,y);
113         ans=max(ans,tr.query_max(1,dfn[top[x]],dfn[x]));
114         tr.update(1,dfn[top[x]],dfn[x],val-1);
115         x=fa[top[x]];
116     }if(dfn[x]>dfn[y]) swap(x,y);
117     ans=max(ans,tr.query_max(1,dfn[x]+1,dfn[y]));
118     tr.update(1,dfn[x]+1,dfn[y],val-1);
119     return ans;
120 }
121 namespace WSN{
122     inline int main(){
123         // freopen("weight1.in","r",stdin);
124         n=read(); m=read(); a=read();
125         for(int i=1;i<=m;i++) mp[i].u=read(),mp[i].v=read(),mp[i].w=read(),mp[i].id=i;
126         kru(); dfs1(0,1); dfs2(1,1); tr.build(1,1,n); int root=getfa(1);
127         for(int i=1;i<=m;i++) if(!vis[mp[i].id]){
128             if(getfa(mp[i].u)!=root) ans[mp[i].id]=0;
129             else ans[mp[i].id]=MAX(mp[i].u,mp[i].v,mp[i].w)-1;
130         }
131         for(int i=1;i<=m;i++) if(vis[mp[i].id]){
132             if(getfa(mp[i].u)!=root) ans[mp[i].id]=0;
133             else{
134                 if(dep[mp[i].u]>dep[mp[i].v]){
135                     ans[mp[i].id]=tr.query_min(1,dfn[mp[i].u]);
136                 }
137                 else ans[mp[i].id]=tr.query_min(1,dfn[mp[i].v]);
138             }
139         }
140         for(int i=1;i<=m;i++){
141             if(ans[i]==inf) printf("-1 ");
142             else printf("%d ",ans[i]);
143         }
144         return 0;
145     }
146 }
147 signed main(){return WSN::main();}
View Code
posted @ 2021-07-21 11:47  雪域亡魂  阅读(87)  评论(0)    收藏  举报