玩水
这个题目感觉似乎不是那么水。。。
绝对不是因为我考场过了。。。
鉴于昨天考试的惨状,我认为打爆力是一个非常有用的方法。
然后自己一上来就只想写一个 \(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();}