1. 程式人生 > 其它 >Noip模擬97 2021.11.14

Noip模擬97 2021.11.14

T1 構造字串 T2 尋寶 T3 序列 T4 構樹

T1 構造字串

看出是圖論,但是沒有試著用冰茶姬,然後特判\(-1\)的還多了,就直接\(40pts\)

然後別人分數都很高就直接底墊墊了。。。。對,又一次底墊墊了

轉化題意很簡單,要求構造一串字典序最小的序列滿足一些數相等,一些數不相等

那麼直接分情況討論,先把要求相等的數按照關係分成一堆聯通塊,然後每一條不相等的關係當做一條邊將聯通塊連起來

不合法的判斷只有一條,並沒有我考場上想的那麼麻煩,直接就如果聯通塊內有連邊就是不合法

然後建完圖之後就是變為了只有不相等的部分分(\(z_i=0\))的情況,那麼直接貪心的按照編號從小往大掃,每次開個桶找個\(mex\)即可

考場上的\(SB\)做法就是因為沒有使用冰茶姬合併相同的關係,而是把相同的和不同的放在了一起處理,這樣會導致錯誤,\(Hack\)

很好造,我很蠢。。。

說一下剛開始的時候想的假做法,沒有保證字典序最小的時候可以對每一條關係整一個邊權,不相等的關係邊權為\(1\),相等的為\(0\),然後每個聯通塊跑一遍\(dfs\)就行了

str
#include<bits/stdc++.h>
#define int long long
using namespace std;FILE *wsn;
auto read=[](){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
};
const int NN=2005;
int n,m,ans[NN],bin[NN];
bool vis[NN];
struct node{
	int a,b,c;
	bool operator<(const node&x)const{
		return c>x.c;
	}
}op[NN];
vector<int>S[NN];
namespace DSU{
	int fa[NN];
	inline int getfa(int x){return fa[x]=((fa[x]==x)?x:getfa(fa[x]));}
	inline void merge(int x,int y){
		x=getfa(x); y=getfa(y);
		if(x==y) return;if(x>y) swap(x,y);
		fa[y]=x;
	}
}using namespace DSU;
namespace WSN{
	inline short main(){
		wsn=freopen("str.in","r",stdin);
		wsn=freopen("str.out","w",stdout);
		n=read(); m=read();
		for(int i=1;i<=n;i++)fa[i]=i;
		for(int i=1,a,b,c;i<=m;i++){
			a=read(),b=read(),c=read();
			if(a>b) swap(a,b);op[i]=node{a,b,c};
		}
		sort(op+1,op+m+1);
		for(int i=1;i<=m&&op[i].c!=0;i++){
			int a=op[i].a,b=op[i].b,c=op[i].c;
			for(int j=1,x,y;j<=c;j++)
				x=a+j-1,y=b+j-1,merge(x,y);
			if(b+c<=n) op[++m]=node{a+c,b+c,0};
		}
		for(int i=m;i>0&&op[i].c==0;i--){
			int a=op[i].a,b=op[i].b,c=op[i].c;
			if(getfa(a)==getfa(b))return puts("-1"),0;
			S[getfa(a)].push_back(getfa(b));
			S[getfa(b)].push_back(getfa(a));
		}
		for(int i=1;i<=n;i++)if(!vis[getfa(i)]){
			int mex=0,x=getfa(i); vis[x]=1;
			memset(bin,0,sizeof(bin));
			for(auto y:S[x])if(y<x) ++bin[ans[y]];
			while(bin[mex]) ++mex;
			ans[x]=mex;
		}
		for(int i=1;i<=n;i++)if(getfa(i)!=i)ans[i]=ans[getfa(i)];
		for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
		puts("");
		return 0;
	}
}
signed main(){return WSN::main();}

\(\color{white}{過於之菜了,已經連著好幾天沒有切題了,而且還都不是不會,就是打不對,很鬱悶。。。。}\)

T2 尋寶

非常無腦的\(bfs+dfs\),切忌把帶著陣列的判斷條件放在判斷下標的前面,否則很可能\(RE\),比如

然後這種送分題都沒能切,於是直接底墊墊。。。。。

然後我的打法比較難調,或者說我打的時候比較慌,出了很多zz錯誤,然後調了好久。。。後面的部分分沒來及打,不打掛的話應該能拿\(50\)左右的樣子。。。

剩下的思路就不說了,學過信隊的,寫過最短路的都可以輕鬆打過(除了我)

treasure
#include<set>
#include<map>
#include<queue>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;FILE *wsn;
int mzs;
#define scanf mzs=scanf
auto read=[](){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
};
const int NN=50005;
int g[NN],n,m,k,q,in[NN],tot;
char ch[NN];
auto id=[](int x,int y){return (x-1)*m+y;};

struct door{int x1,y1,x2,y2;}d[105];

typedef pair<int,int> PII;
#define fi first
#define se second
#define mpr make_pair
queue<PII> Q;
map<int,PII>mp;
set<int> jump[NN];
bitset<NN>ee[NN];

int dx[5]={0,1,-1,0,0},
	dy[5]={0,0,0,1,-1};
bool vis[NN];
inline void walk(int x,int y){
	Q.push(mpr(x,y)); vis[id(x,y)]=1;
	in[id(x,y)]=++tot;
	while(!Q.empty()){
		int x=Q.front().fi,y=Q.front().se; Q.pop();
		for(int i=1;i<=4;i++){
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>0&&yy>0&&xx<=n&&yy<=m&&!vis[id(xx,yy)]&&!g[id(xx,yy)]){
				Q.push(mpr(xx,yy));
				vis[id(xx,yy)]=1; in[id(xx,yy)]=tot;
			}
		}
	}
}
inline void dfs(int x){
	vis[x]=1;
	for(auto y:jump[x]){
		if(vis[y]) continue;
		dfs(y);
		ee[x]|=ee[y];
	}
}
namespace WSN{
	inline short main(){
		wsn=freopen("treasure.in","r",stdin);
		wsn=freopen("treasure.out","w",stdout);
		n=read(); m=read(); k=read(); q=read();
		for(int i=1;i<=n;i++){
			scanf("%s",ch+1);
			for(int j=1;j<=m;j++)
				g[id(i,j)]=(ch[j]=='#');
		}
		for(int i=1;i<=k;i++){
			int x1=read(),y1=read(),x2=read(),y2=read();
			d[i]=door{x1,y1,x2,y2};mp[id(x1,y1)]=mpr(x2,y2);
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(!vis[id(i,j)]&&!g[id(i,j)]){
					walk(i,j);
				}
			}
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(mp.find(id(i,j))!=mp.end()){
					PII now=mp[id(i,j)];
					jump[in[id(i,j)]].insert(in[id(now.fi,now.se)]);
					ee[in[id(i,j)]][in[id(now.fi,now.se)]]=1;
				}
			}
		}
		for(int i=1;i<=tot;i++){
			memset(vis,0,sizeof(vis));
			dfs(i);
		}
		for(int i=1;i<=q;i++){
			int x1=read(),y1=read(),x2=read(),y2=read();
			int i1=in[id(x1,y1)],i2=in[id(x2,y2)];
			if(i1==i2){puts("1");continue;}
			if(ee[i1][i2]){puts("1");}
			else puts("0");
		}
		return 0;
	}
}
signed main(){return WSN::main();}

T3 序列

李超線段樹維護線段

考慮把過\(p\)區間分成右端點在\(p\)的一段最優區間加上左端點在\(p+1\)的一段最優區間

那麼每次對答案的統計就變成了固定一個點,找另一個點,這樣預計得到\(50\)

那麼嘗試化簡式子

\(sa_i=\sum_{j=1}^i a_j\)\(sb_i=\sum_{j=1}^ib_j\)

最優即\(\max_{1\leq l\leq r}\{(sa_{r}-sa_{l-1})-k(sb_{r}-sb_{l-1})\}\)

\(sa_r-k\cdot sb_{r}-\min_{0\leq l<r}\{ksb_{l}-sa_l\}\),離線之後李超樹維護直線即可

時間複雜度為\(O(n\log n)\),常數略大,空間複雜度為\(O(n)\)

seq
#include<bits/stdc++.h>
#define int long long
using namespace std; FILE *wsn;
auto read=[](){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
};
const int NN=2e6+5,inf=1e18;
const int lim=1e6;
int n,m,a[NN],b[NN],ans[NN],lst;
int sufa[NN],sufb[NN],prea[NN],preb[NN];
struct Query{
	int p,k,id;
	bool operator<(const Query&x)const{
		return p==x.p?k<x.k:p<x.p;
	}
}qu[NN];
namespace segment_tree{
	#define lid (id<<1)
	#define rid (id<<1|1)
	#define mid ((l+r)>>1)
	struct seg{
		int k,b;
		seg(){k=lim;b=inf;}
		seg(int k,int b):k(k),b(b){}
		int calc(int x){return k*x+b;}
	}tr[NN<<2];
	inline void update(seg x,int id=1,int l=-lim,int r=lim){
		if(tr[id].calc(mid)>x.calc(mid)) swap(tr[id],x);
		if(tr[id].calc(l)>x.calc(l))update(x,lid,l,mid);
		if(tr[id].calc(r)>x.calc(r))update(x,rid,mid+1,r);
	}
	inline int query(int pos,int id=1,int l=-lim,int r=lim){
		if(l>pos||r<pos) return inf;
		if(l==r) return tr[id].calc(pos);
		return min((mid<pos?query(pos,rid,mid+1,r):query(pos,lid,l,mid)),tr[id].calc(pos));
	}
}using namespace segment_tree;
namespace WSN{
	inline short main(){
		wsn=freopen("seq.in","r",stdin);
		wsn=freopen("seq.out","w",stdout);
		n=read(); m=read();
		for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
		for(int i=1;i<=n;i++)prea[i]=prea[i-1]+a[i],preb[i]=preb[i-1]+b[i];
		for(int i=n;i;i--)sufa[i]=sufa[i+1]+a[i],sufb[i]=sufb[i+1]+b[i];
		for(int o=1,p,k;o<=m;o++) p=read(),k=read(),qu[o]=Query{p,k,o};
		sort(qu+1,qu+m+1);
		lst=0;
		for(int i=1;i<=m;i++){
			Query x=qu[i];
			for(int j=lst;j<x.p;j++)
				update(seg{-preb[j],prea[j]});
			ans[x.id]=prea[x.p]-x.k*preb[x.p]-query(x.k);
			lst=x.p;
		}
		memset(tr,0,sizeof(tr));
		lst=n+1;
		for(int i=m;i;i--){
			Query x=qu[i];
			for(int j=lst;j>=x.p+1;j--)
				update(seg{-sufb[j],sufa[j]});
			ans[x.id]+=sufa[x.p+1]-x.k*sufb[x.p+1]-query(x.k);
			lst=x.p+1;
		}
		for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
		return 0;
	}
}
signed main(){return WSN::main();}

T4 構樹

正在改。。。咕咕咕