[考试总结]noip模拟68(多校)

玩水

这个题目感觉似乎不是那么水。。。

绝对不是因为我考场过了。。。

鉴于昨天考试的惨状,我认为打爆力是一个非常有用的方法。

然后自己一上来就只想写一个 \(20pts\)\(n==2\) 的垃圾部分分数。

所以分析了一下。。。

发现如果只要有一个连着的两个斜着相等的就是合法的。

然后发现可以推广。

我们只需要找到不在同一行或者是同一列的两个斜着的东西就行了。

或者就是在同一行列的连着的两个。

所以我们使用 \(multiset\) 维护一下所有的这样连着的位置。

然后发现会找错,然后我们就开两个。


#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
	#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
	#define sb(x) cout<<#x" = "<<x<<' '
	#define jb(x) cout<<#x" = "<<x<<endl
	#define debug cout<<"debug"<<endl
	#define gec() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
	#define gc() getchar()
	#define scanf ak = scanf
	char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
	class xin_stream{public:template<typename type>xin_stream &operator >> (type &s)
	{
		s = 0; register bool f = 0; register char ch = gc();
		while(!isdigit(ch)) f |= ch == '-',ch = gc();
		while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
	}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 2e3+10,inf = 1e9+10;
//#define int long long
namespace xin
{
	int T,n,m;
	char a[two][two];
	inline void sp()
	{
		try(i,1,m-2)
			if(a[2][i] == a[1][i+1] and a[2][i+1] == a[1][i+2])
			{puts("1"); return;}
		puts("0");
	}
	class xin_data
	{
		private:
			friend bool operator < (xin_data x,xin_data y)
			{return (x.x == y.x) ? x.y < y.y : x.x > y.x;}
		public:
			int x,y;
			xin_data(){}
			xin_data(int x,int y):x(x),y(y){}
	};
	class xin_data2
	{
		private:
			friend bool operator < (xin_data2 x,xin_data2 y)
			{return (x.x == y.x) ? x.y > y.y : x.x > y.x;}
		public:
			int x,y;
			xin_data2(){}
			xin_data2(int x,int y):x(x),y(y){}
	};
	std::multiset<xin_data>s;
	std::multiset<xin_data2>s2;
	inline void work()
	{
//		register int x = 0,y = 0;
		try(i,2,n) try(j,1,m-2)
			if(a[i][j] == a[i-1][j+1] and a[i][j+1] == a[i-1][j+2])
				{puts("1"); return;}
		try(i,2,n) try(j,1,m-1)
			if(a[i][j] == a[i-1][j+1] and a[i+1][j] == a[i][j+1])
				{puts("1"); return;}
		try(i,2,n)
		{
			try(j,1,m-1)
			{
				if(a[i][j] == a[i-1][j+1])
				{
					auto it = s.lower_bound(xin_data(i-1,j-1));
//					for(auto p = s.begin();p != s.end();p++) cout<<(p -> x)<<' '<<(p -> y)<<endl;
//					sb(i-1); jb(j-1);
					if(it != s.end())
					{
						register int nx = it -> x,ny = it -> y;
//						sb(i); sb(j); sb(nx); jb(ny);
						if(nx < i and ny < j) {puts("1"); return;}
					}
					auto it2 = s2.lower_bound(xin_data2(i-1,j-1));
					if(it2 != s2.end())
					{
						register int nx = it2 -> x,ny = it2 -> y;
						if(nx < i and ny < j) {puts("1"); return;}
					}
					s.insert(xin_data(i,j)); s2.insert(xin_data2(i,j));
				}
			}
		}
		puts("0");
	}
	inline short main()
	{
		freopen("water.in","r",stdin); freopen("water.out","w",stdout);
		scanf("%d",&T);
//		s.insert(xin_data(1,1));
//		auto it = s.lower_bound(xin_data(2,3));
//		cout<<(it -> x)<<' '<<(it -> y)<<endl;
		while(T--)
		{
			scanf("%d%d",&n,&m); s.clear(); s2.clear();
			try(i,1,n) scanf("%s",a[i]+1);
			if(n == 2) sp();
			else work();
		}
		return 0;
	}
}
signed main() {return xin::main();}

切题

根据最大流最小割定理,满流等价于最小割为 \(\sum_{i=1}^{n}a_i\)。不妨把 \(a\) 从大到小排序,那么满流就等价于对于任意的 \(k \in [0, n]\),有 \(\sum_{i=1}^{k}a_i \leq \sum_{i=1}^{m}\min(b_i,k)\)
考虑优化,设 \(c_k\) 表示满足 \(b_i \geq k\)\(b_i\) 个数。那么 \(\sum_{i=1}^{m} min(b_i,k) = \sum_{i=1}^{k}c_i\)我们就是要判断 \(\sum_{i=1}^{k}(c_i −a_i) \geq 0\) 是否对任意 \(k\) 都成立。可以使用线段树来维护其最小值。
对于 1, 2 操作,从大到小维护 \(a_i\),我们只需找到第一次/最后一次 \(a_i\) 出现的位置,并将其加/减 \(1\) 即可。可以通过 BIT 等方法来找位置,然后在线段树上区间修改即可。
对于 3, 4 操作,只有 \(c_{b_i}\) 及其相邻的两个位置可能会有变化,直接在线段树上区间修改即可。
不妨把 \(n, m, q, a, b\) 看作同阶,那么时间复杂度为 \(\mathcal O(n\log n)\)



#include<bits/stdc++.h>
using std::cout; using std::endl;
#define try(i,a,b) for(register int i=a;i<=b;++i)
#define throw(i,a,b) for(register int i=a;i>=b;--i)
#define go(i,x) for(register signed i=head[x],y=edge[i].ver;i;i=edge[i].next,y=edge[i].ver)
namespace xin_io
{
	#define file(x) FILE *FI = freopen(#x".in","r",stdin); FI = freopen(#x".out","w",stdout);
	#define sb(x) cout<<#x" = "<<x<<' '
	#define jb(x) cout<<#x" = "<<x<<endl
	#define debug cout<<"debug"<<endl
	#define gec() p1 == p2 and (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin),p1 == p2) ? EOF : *p1 ++
	#define gc() getchar()
	#define scanf ak = scanf
	char buf[1<<20],*p1 = buf,*p2 = buf; using ll = long long; using ull = unsigned long long; int ak;
	class xin_stream{public:template<typename type>xin_stream &operator >> (type &s)
	{
		s = 0; register bool f = 0; register char ch = gc();
		while(!isdigit(ch)) f |= ch == '-',ch = gc();
		while( isdigit(ch)) s = (s << 1) + (s << 3) + (ch xor 48),ch = gc(); return s = f ? -s : s,*this;
	}}io;
}
using namespace xin_io; static const int maxn = 1e6+10,two = 4e3+10,inf = 1e9+10;
#define int long long
namespace xin
{
	#define ls(fa) (fa << 1)
	#define rs(fa) (fa << 1 | 1)
	int n,m,p[maxn],a[maxn],b[maxn],c[maxn],pai[maxn],qnum;
	class xin_tree{public:int s,debt;}t[maxn];
	auto min = [](int x,int y) -> int{return x < y ? x : y;};
	auto up = [](int fa) -> void{t[fa].s = min(t[ls(fa)].s,t[rs(fa)].s);};
	auto down = [](int fa) -> void
	{
		if(!t[fa].debt) return ;
		t[ls(fa)].s += t[fa].debt; t[rs(fa)].s += t[fa].debt;
		t[ls(fa)].debt += t[fa].debt; t[rs(fa)].debt += t[fa].debt;
		t[fa].debt = 0;
	};
	inline void build(int fa,int l,int r)
	{
		if(l == r) return t[fa].s = p[l],void();
		register int mid = l + r >> 1;
		build(ls(fa),l,mid); build(rs(fa),mid+1,r);
		up(fa);
	}
	inline void update(int fa,int l,int r,int ql,int qr,int val)
	{
		if(ql <= l and qr >= r)
		{
			t[fa].s += val; t[fa].debt += val;
			return ;
		}
		down(fa);
		register int mid = l + r >> 1;
		if(ql <= mid) update(ls(fa),l,mid,ql,qr,val);
		if(qr >  mid) update(rs(fa),mid+1,r,ql,qr,val);
		up(fa);
	}
	inline short main()
	{
		file(problem);
		io >> n >> m;
		try(i,1,n) io >> a[i],pai[i] = a[i]; try(i,1,m) io >> b[i],c[b[i]] ++;
		std::sort(pai+1,pai+n+1,[](int x,int y) -> bool {return x > y;});
		throw(i,250000,1) c[i] += c[i+1];
		try(i,1,n) p[i] = p[i-1] + (c[i] - pai[i]);
		std::sort(pai+1,pai+n+1);
		build(1,1,n);
//		jb(query(1,1,n,1,n));
		io >> qnum;
		try(cse,1,qnum)
		{
			register int op,x; io >> op >> x;
			if(op == 1)
			{
				register int pos = std::upper_bound(pai+1,pai+n+1,a[x]) - pai; pai[--pos]++; a[x] = pai[pos];
				pos = n - pos + 1;
				update(1,1,n,pos,n,-1);
				printf(t[1].s >= 0 ? "1" : "0");
			}
			else if(op == 2)
			{
				register int pos = std::lower_bound(pai+1,pai+n+1,a[x]) - pai; pai[pos] --; a[x] = pai[pos];
				pos = n - pos + 1;
				update(1,1,n,pos,n,1);
				printf(t[1].s >= 0 ? "1" : "0");
			}
			else if(op == 3)
			{
				b[x] ++;
				update(1,1,n,b[x],n,1);
				printf(t[1].s >= 0 ? "1" : "0");
			}
			else
			{
				b[x] --;
				update(1,1,n,b[x]+1,n,-1);
				printf(t[1].s >= 0 ? "1" : "0");
			}
			putchar('\n');
		}
		return 0;
	}
}
signed main() {return xin::main();}
posted @ 2021-10-06 21:15  NP2Z  阅读(43)  评论(0编辑  收藏  举报