noip96

T1

多测不清空,爆零两行泪。

考场想法过于sb,树上带修莫队+查询区间 \(\min/\max\) ,然后就不会了。

结果写的暴力多测没清空,爆零了....改了之后竟然能切?

判断排列直接写个hash就行了...

T2

考场以为是可追溯化并查集之前颓游记的时候发现的,然而并不会,想了想,发现可以用个栈存下操作,然后就不知道该怎么处理了,打了60pts就跑路了。

竟然是套路题

有个东西叫线段树分治,就是维护每个操作能产生影响的区间,统计答案dfs一遍,回溯时撤销当前操作影响。

把每一个操作插入到其能产生影响的区间中,然后用个可撤销并查集维护 \(size\) 大小。

可撤销并查集,就是用个栈存下操作,不能用路径压缩,不然会出错,要用启发式合并来保证复杂度。

Code
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
using std::sort;
using std::pair;
using std::vector;
using std::upper_bound;
#define fi first
#define se second
using std::make_pair;
#define pack make_pair
#define push emplace_back
typedef pair<int,int> my;
#define re register
const int MAX = 1e5+3;
const int mod = 1e9+7;
#define long long long
#define scanf oma=scanf
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	int n,m;
	long inv[MAX],ans=1;
	struct data
	{ int op,u,v; }q[MAX];
	vector<pair<my,int>>s;
	#define debug(s) printf("%s\n",s)
}using namespace some;
namespace Data_Structures
{
	auto swap = [](int &a,int &b) { int t=a; a=b,b=t; };
	struct DSU
	{
		int top;
		my sta[MAX];
		int fa[MAX],size[MAX];
		void init()
		{ for(re int i=1; i<=n; i++) { size[fa[i] = i] = 1; } }
		int find(int x)
		{ return x!=fa[x]?find(fa[x]):fa[x]; }
		void merge(int x,int y)
		{
			x = find(x),y = find(y);
			if(x!=y)
			{
				if(size[x]<size[y])
				{ swap(x,y); }
				fa[y] = x,ans = ans*inv[size[x]]%mod*inv[size[y]]%mod*(size[x] += size[y])%mod;
			}
			sta[++top] = pack(x,y);
		}
		void revoke(int cnt)
		{
			int x,y; my now;
			//debug("<---->");
			while(cnt)
			{
				cnt--;
				now = sta[top--],x = now.fi,y = now.se;
				if(x==y)
				{ continue ; }
				//printf("x=%d y=%d sizex=%d sizey=%d\n",x,y ,size[x],size[y]);
				ans = ans*inv[size[x]]%mod*(size[x] -= size[y])%mod*size[fa[y] = y]%mod;
			}
			//debug(">----<");
		}
	}ds;
	struct Segment_Tree
	{
		vector<my>val[MAX<<2];
		#define ls(p) p<<1
		#define rs(p) p<<1|1
		#define mid (l+r>>1)
		void insert(int p,int l,int r,int lp,int rp,my w)
		{
			if(lp<=l&&r<=rp)
			{ val[p].push(w); return ; }
			if(lp<=mid)
			{ insert(ls(p),l,mid,lp,rp,w); }
			if(rp>mid)
			{ insert(rs(p),mid+1,r,lp,rp,w); }
		}
		void dfs(int p,int l,int r)
		{
			for(auto x : val[p])
			{ ds.merge(x.fi,x.se); }
			if(l==r)
			{ printf("%lld\n",ans); goto jump; }
			dfs(ls(p),l,mid),dfs(rs(p),mid+1,r);
			jump: ;
			/*printf("l=%d r=%d\n",l,r);*/ ds.revoke(val[p].size());
		}
	}Tree;
}using namespace Data_Structures;
namespace OMA
{
	auto main = []() -> signed
	{
		//#define local
		#ifdef local
		debug("look here! if you want submit,please closed this");
		freopen("node.in","r",stdin); freopen("my.out","w",stdout);
		#endif
		freopen("b.in","r",stdin); freopen("b.out","w",stdout);
		cin >> n >> m; inv[1] = 1;
		for(re int i=1; i<=m; i++)
		{
			cin >> q[i].op >> q[i].u >> q[i].v;
			if(q[i].u>q[i].v)
			{ swap(q[i].u,q[i].v); }
			if(q[i].op==2)
			{ s.push(pack(pack(q[i].u,q[i].v),i)); }
		}
		sort(s.begin(),s.end());
		for(re int i=1; i<=m; i++)
		{
			if(q[i].op==1)
			{
				int r;
				int pos = upper_bound(s.begin(),s.end(),pack(pack(q[i].u,q[i].v),i))-s.begin();
				if(pos==s.size())
				{ Tree.insert(1,1,m,i,m,pack(q[i].u,q[i].v)); continue ; }
				pair<my,int> p = s[pos];
				if(p.fi!=pack(q[i].u,q[i].v))
				{ Tree.insert(1,1,m,i,m,pack(q[i].u,q[i].v)); /*r = m;*/ }
				else
				{ Tree.insert(1,1,m,i,p.se-1,pack(q[i].u,q[i].v)); /*r = p.se-1; printf("%d %d\n",i,p.se);*/ }
				//printf("l=%d r=%d\n",i,r);
			}
		}
		for(re int i=2; i<=n; i++)
		{ inv[i] = (mod-mod/i)*inv[mod%i]%mod; }
		ds.init(); Tree.dfs(1,1,m);
		return 0;
	};
};
signed main()
{ return OMA::main(); }

T3

考场啥也不会,cdq也没写出来...

先考虑一个这样一个问题,给定三个排列 \(a,b,c\) ,求满足 \(a_{i}<a_{j},b_{i}<b_{j},c_{i}<c_{j}\) 的 \((i,j)\) 个数,其中 \(a\) 序列为下标序列,直接写cdq肯定不行。

用题解做法 对 \((a,b),(a,c),(b,c)\) 这三对分别求一遍二维偏序,那么每一对 \((i,j)\) 不是满足二维偏序,就是满足三维偏序,因为 \(a\) 序列为下标,肯定满足 \(i<j\) , \(b_{i},b_{j}\) 之间的关系只有大于小于, \(c_{i},c_{j}\) 同理,俩都大于交换一下就行。

那么这样在统计给这三对二维偏序求和时,如果 \((i,j)\) 满足的是三维偏序关系,会被算三遍,否则只会算一遍。

设二维偏序之和为 \(m\) , 所以三维偏序的数量就是 \(\frac{m-\tbinom{n}{2}}{2}\) 。

这是没有-1的情况,有-1的情况,则预处理出从 \(q\) 没有的数中选,比 \(i\) 大的概率,用树状数组维护的时候插概率就行了,如果都是-1,那么概率就是 \(\frac{1}{2}\) 。因为贡献是1,所以期望就是概率。

不过战神的容斥做法更好搞一些,也更好理解。

Code
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
using std::sort;
using std::vector;
#define re register
#define pack push_back
const int MAX = 1e6+3;
#define long long long
#define scanf oma=scanf
const int mod = 998244353;
const int inv2 = 499122177;
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	long res;
	long rec[MAX];
	bool tub[MAX];
	int seq[MAX],tot;
	int n,p[MAX],q[MAX];
	auto quickpow = [](int a,int b,int res = 1)
	{
		while(b)
		{
			if(b&1)
			{ res = 1ll*res*a%mod; }
			a = 1ll*a*a%mod,b >>= 1;
		}
		return res;
	};
	#define debug(s) printf("%s\n",s)
}using namespace some;
namespace Data_Structures
{
	struct BIT
	{
		int val[MAX];
		#define lowbit(x) x&-x
		void clear()
		{ memset(val,0,sizeof(int)*(n+1)); }
		void modify(int x,int w)
		{ for(re int i=x; i<=n; i+=lowbit(i)) { (val[i] += w) %= mod; } }
		int query(int x,int res = 0)
		{ for(re int i=x; i; i-=lowbit(i)) { (res += val[i]) %= mod; } return res; }
	}Tree;
}using namespace Data_Structures;
namespace OMA
{
	auto main = []() -> signed
	{
		//#define local
		#ifdef local
		debug("look here! if you want submit,please closed this");
		freopen("node.in","r",stdin); freopen("my.out","w",stdout);
		#endif
		freopen("c.in","r",stdin); freopen("c.out","w",stdout);
		cin >> n;
		for(re int i=1; i<=n; i++)
		{ cin >> p[i]; }
		for(re int i=1; i<=n; i++)
		{ cin >> q[i]; if(q[i]!=-1) { seq[++tot] = i; tub[q[i]] = true; } }
		for(re int i=1; i<=tot; i++)
		{ (res += Tree.query(p[seq[i]])) %= mod; Tree.modify(p[seq[i]],1); }
		Tree.clear();
		for(re int i=1; i<=tot; i++)
		{ (res += Tree.query(q[seq[i]])) %= mod; Tree.modify(q[seq[i]],1); }
		Tree.clear(); sort(seq+1,seq+1+tot,[](const int &x,const int &y) { return q[x]<q[y]; });
		for(re int i=1; i<=tot; i++)
		{ (res += Tree.query(p[seq[i]])) %= mod; Tree.modify(p[seq[i]],1); }
		//printf("res=%lld\n",res);
		res = (res-1ll*tot*(tot-1)%mod*inv2%mod+mod)%mod*inv2%mod,tot = 0;
		//printf("res=%lld\n",res);
		for(re int i=1; i<=n; i++)
		{
			if(!tub[i])
			{ seq[++tot] = i; /*printf("i=%d ",i);*/ }
		}
		//printf("\ntot=%d\n",tot);
		long inv = quickpow(tot,mod-2);
		for(re int i=1,pos=1; i<=n; i++)
		{
			while(pos<=tot&&seq[pos]<=i)
			{ pos++; }
			//printf("%d\n",pos);
			rec[i] = (tot-pos+1)*inv%mod;
			//printf("rec[%d]=%lld\n",i,rec[i]);
		}
		Tree.clear();
		for(re int i=1; i<=n; i++)
		{
			if(q[i]!=-1)
			{ Tree.modify(p[i],rec[q[i]]); }
			else
			{ (res += Tree.query(p[i])) %= mod; }
		}
		Tree.clear();
		for(re int i=n; i; i--)
		{
			if(q[i]!=-1)
			{ Tree.modify(p[i],mod+1-rec[q[i]]); }
			else
			{ (res += Tree.query(n)-Tree.query(p[i])+mod) %= mod; }
		}
		Tree.clear();
		for(re int i=1; i<=n; i++)
		{ if(q[i]==-1) { (res += Tree.query(p[i])) %= mod; Tree.modify(p[i],inv2); } }
		printf("%lld\n",res);
		return 0;
	};
};
signed main()
{ return OMA::main(); }

T4

不会,咕...

noip96

上一篇:(多校)超级加倍


下一篇:(联考)noip94