题解 a

传送门

真·就我切不掉签到题
其实口头切了,但做法太恶心了所以写不出来

发现每个位置最多进行一次操作 1
所以 check 一个区间可以贪心,看会不会有减成负数或弄不成偶数的地方
没有 0 的话就只需要考虑有没有弄不成偶数的地方
一个区间合法的条件是区间内有偶数个奇数
容易想到扫描线+线段树历史和,需要支持区间 0/1 取反和区间历史和
然而……

  • 对于线段树区间 0/1 取反和区间历史和:
    其实并不需要真的写历史和,只需要开两棵线段树,每棵每个位置的权值为 \([这个位置应该在这棵线段树上]*val(i)\) 即可
    也可以推广到轮换之类

那么其实直接扫描线,在两棵线段树上区间加就好了

然后考虑有 0 怎么做
正经做法是继续扫描线,那么找到前面一个 0 和前面最近的一个 包含了奇数个 1 的区间的左端点的 0
那么可以用这两个 0 确定能产生贡献的区间,根据前缀和奇偶性选线段树做区间加即可

接下来是赛时的鬼畜做法(但应该是对的):
考虑在线处理询问
将极长无 0 段和极长全 0 段称为一个块
那么每个块内的贡献可以线段树历史和预处理
然后考虑跨块的子区间
称一个包含奇数个 1 的块为坏块
那么相邻坏块会将块的序列分为若干个大块
令在每个块中选一个包含偶数个 1 的后缀方案数为 \(f_i\),选一个合法前缀方案数为 \(g_i\)
那么一个大块的贡献是

\[\sum\limits_if_i\sum\limits_{j>i}g_j \]

可以预处理
然后考虑两个边角
合法区间为 总合法区间 - 左端点在 r 以右的(可以对每个块预处理乘积的后缀和)- \(\sum\limits_{i}^{r-1}f_i\sum\limits_{j>r}g_j\)
于是就可以做了,复杂度也是 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans;
}

int n, q;
int a[N];

namespace force{
	int b[N];
	bool check(int l, int r) {
		for (int i=l; i<=r; ++i) b[i]=a[i];
		for (int i=l; i<=r; ++i) {
			if (b[i]<0) return 0;
			if (b[i]&1) {
				if (i==r) return 0;
				else --b[i], --b[i+1];
			}
		}
		return 1;
	}
	void solve() {
		for (int i=1,l,r; i<=q; ++i) {
			l=read(); r=read();
			int ans=0;
			for (int j=l; j<=r; ++j)
				for (int k=j; k<=r; ++k)
					ans+=check(j, k);
			printf("%d\n", ans);
		}
	}
}

namespace task1{
	vector<int> able[N];
	vector<pair<int, int>> que[N];
	int ans[N], val[N], sum[N], b[N];
	void solve() {
		for (int i=1; i<=n; ++i) {
			for (int j=i; j<=n; ++j) b[j]=a[j];
			for (int j=i; j<=n; ++j) {
				if (b[j]<0) break;
				if (b[j]&1) --b[j], --b[j+1];
				else able[i].pb(j);
			}
		}
		for (int i=1,l,r; i<=q; ++i) {
			l=read(); r=read();
			que[l].pb({r, i});
		}
		for (int i=n; i; --i) {
			for (auto& it:able[i]) ++val[it];
			for (int j=i; j<=n; ++j) sum[j]=sum[j-1]+val[j];
			for (auto& it:que[i]) ans[it.sec]=sum[it.fir];
		}
		for (int i=1; i<=q; ++i) printf("%d\n", ans[i]);
	}
}

namespace task2{
	bool val[N], rev[N];
	int bel[N], ls[N], rs[N], len;
	vector<pair<int, int>> que[N];
	ll ans[N], his[N], his_b[N], sum[N], siz[N], rsiz[N], cnt[N], rcnt[N];
	int tem=0;
	void build(int id) {
		for (int i=ls[id]; i<=rs[id]; ++i) {
			his_b[id]+=his[i];
			siz[id]+=val[i];
		}
		rsiz[id]=(rs[id]-ls[id]+1)-siz[id];
		// ++tem;
		// sum[id]=his_b[id]+siz[id]*cnt[id]+((rs[id]-ls[id]+1)-siz[id])*rcnt[id];
	}
	void upd(int l, int r, int dat) {
		int sid=bel[l], eid=bel[r];
		if (sid==eid) {
			for (int i=l; i<=r; ++i) his[i]+=(val[i]^=dat);
			if (l==ls[sid]) build(sid);
			return ;
		}
		for (int i=l; bel[i]==sid; ++i) his[i]+=(val[i]^=dat);
		for (int i=sid+1; i<=eid; ++i) {
			rev[i]^=dat;
			if (rev[i]) ++rcnt[i];
			else ++cnt[i];
		}
		if (l==ls[sid]) build(sid);
	}
	ll query(int l, int r) {
		ll ans=0;
		int sid=bel[l], eid=bel[r];
		if (sid==eid) {
			for (int i=l; i<=r; ++i) {
				ans+=his[i];
				if (val[i]) ans+=cnt[sid];
				else ans+=rcnt[sid];
			}
			return 1ll*(r-l+1)*(r-l+2)/2-ans;
		}
		for (int i=l; bel[i]==sid; ++i) {
			ans+=his[i];
			if (val[i]) ans+=cnt[sid];
			else ans+=rcnt[sid];
		}
		for (int i=sid+1; i<eid; ++i) ans+=his_b[i]+siz[i]*cnt[i]+rsiz[i]*rcnt[i];
		// for (int i=sid+1; i<eid; ++i) ans+=sum[i];
		for (int i=r; bel[i]==eid; --i) {
			ans+=his[i];
			if (val[i]) ans+=cnt[eid];
			else ans+=rcnt[eid];
		}
		return 1ll*(r-l+1)*(r-l+2)/2-ans;
	}
	// void upd(int l, int r, int dat) {
	// 	for (int i=l; i<=r; ++i) his[i]+=(val[i]^=dat);
	// }
	// ll query(int l, int r) {
	// 	ll ans=0;
	// 	for (int i=l; i<=r; ++i) ans+=his[i];
	// 	return 1ll*(r-l+1)*(r-l+2)/2-ans;
	// }
	void solve() {
		len=sqrt(n);
		memset(ls, 0x3f, sizeof(ls));
		for (int i=1; i<=n; ++i) bel[i]=(i-1)/len+1;
		for (int i=1; i<=n; ++i) ls[bel[i]]=min(ls[bel[i]], i), rs[bel[i]]=max(rs[bel[i]], i);
		for (int i=1,l,r; i<=q; ++i) {
			l=read(); r=read();
			que[l].pb({r, i});
		}
		for (int i=n; i; --i) {
			// cout<<"i: "<<i<<endl;
			if (a[i]&1) upd(i, n, 1);
			else upd(i, n, 0);
			// cout<<"his: "; for (int j=1; j<=n; ++j) cout<<his[j]<<' '; cout<<endl;
			for (auto it:que[i]) ans[it.sec]=query(i, it.fir);
		}
		for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
		// cerr<<"tem: "<<tem<<endl;
	}
}

namespace task{
	ll ans[N], sum[N];
	vector<pair<int, int>> que[N];
	struct segment{
		int tl[N<<2], tr[N<<2];
		ll k[N<<2], sum[N<<2], tag[N<<2];
		#define tl(p) tl[p]
		#define tr(p) tr[p]
		#define pushup(p) k[p]=k[p<<1]+k[p<<1|1], sum[p]=sum[p<<1]+sum[p<<1|1]
		void spread(int p) {
			if (!tag[p]) return ;
			sum[p<<1]+=k[p<<1]*tag[p]; tag[p<<1]+=tag[p];
			sum[p<<1|1]+=k[p<<1|1]*tag[p]; tag[p<<1|1]+=tag[p];
			tag[p]=0;
		}
		void build(int p, int l, int r) {
			tl(p)=l; tr(p)=r;
			if (l==r) return ;
			int mid=(l+r)>>1;
			build(p<<1, l, mid);
			build(p<<1|1, mid+1, r);
		}
		void enable(int p, int pos) {
			if (tl(p)==tr(p)) {k[p]=1; return ;}
			spread(p);
			int mid=(tl(p)+tr(p))>>1;
			if (pos<=mid) enable(p<<1, pos);
			else enable(p<<1|1, pos);
			pushup(p);
		}
		void upd(int p, int l, int r) {
			if (l<=tl(p)&&r>=tr(p)) {sum[p]+=k[p]; ++tag[p]; return ;}
			spread(p);
			int mid=(tl(p)+tr(p))>>1;
			if (l<=mid) upd(p<<1, l, r);
			if (r>mid) upd(p<<1|1, l, r);
			pushup(p);
		}
		ll query(int p, int l, int r) {
			if (l<=tl(p)&&r>=tr(p)) return sum[p];
			spread(p);
			int mid=(tl(p)+tr(p))>>1; ll ans=0;
			if (l<=mid) ans+=query(p<<1, l, r);
			if (r>mid) ans+=query(p<<1|1, l, r);
			return ans;
		}
	}seg[2];
	void solve() {
		for (int i=1; i<=n; ++i) sum[i]=sum[i-1]+a[i];
		for (int i=1,l,r; i<=q; ++i) {
			l=read(); r=read();
			que[r].pb({l, i});
		}
		seg[0].build(1, 0, n); seg[1].build(1, 0, n);
		seg[0].enable(1, 0);
		int pos=0, lst=0;
		for (int i=1,cnt=0; i<=n; ++i) {
			// cout<<"i: "<<i<<' '<<lst<<endl;
			if (a[i]&1) ++cnt;
			seg[sum[i]&1].enable(1, i); //, cout<<"enable: "<<(sum[i]&1)<<' '<<i<<endl;
			if (cnt&1) seg[sum[i]&1].upd(1, lst, i-1); //, cout<<1<<' '<<lst+1<<' '<<i-(a[i]&1)<<endl;
			else seg[sum[i]&1].upd(1, pos, i-1); //, cout<<2<<endl;
			if (!a[i]) {
				if (cnt&1) pos=lst;
				lst=i; cnt=0;
			}
			for (auto it:que[i]) {
				// cout<<it.fir<<' '<<it.sec<<endl;
				ans[it.sec]+=seg[0].query(1, it.fir-1, i)+seg[1].query(1, it.fir-1, i);
			}
		}
		for (int i=1; i<=q; ++i) printf("%lld\n", ans[i]);
	}
}

signed main()
{
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);

	n=read(); q=read();
	bool no_zero=1;
	for (int i=1; i<=n; ++i) if ((a[i]=read())==0) no_zero=0;
	// force::solve();
	// if (n<=5000) task1::solve();
	// else task2::solve();
	task::solve();

	return 0;
}
posted @ 2022-05-03 07:03  Administrator-09  阅读(2)  评论(0编辑  收藏  举报