JOISC 2020 DAY2

Day 2

T1

二分图的部分分直接二分可以得到三个点,一个入度一个出度一个颜色相等。

然后你可以询问两次看颜色相等和入度是哪两个(询问答案是1),就能做了。

非二分图每次暴力看能不能加入当前集合,然后再把剩下的点查询边并且对剩下的点递归下去。

容易证明次数是 \(3n\log n + 3n + 2n + 2n\) 左右。

#include "chameleon.h"
#include<bits/stdc++.h>
using namespace std;

vector<int> st[1010];
bool in[1010];
int c[1010][1010];
map<vector<int>,int> Q;
int Tim;
int Qry(vector<int> s){
	sort(s.begin(),s.end());
	if(Q.find(s)!=Q.end())return Q[s];
	++Tim;
	return Q[s]=Query(s);
}
inline void Div(vector<int> s,int added){
	if(!s.size())return ;
	if(s.size()==1){
		st[added].push_back(s[0]);
		st[s[0]].push_back(added);
		return ;
	}
	vector<int> Div1,Div2;
	int sz = s.size();
	for(int i=0;i<sz;i++){
		if(i<sz/2)Div1.push_back(s[i]);
		else Div2.push_back(s[i]);
	}
	if(Div1.size()){
		Div1.push_back(added);
		int q1=Div1.size()-1;
		int q2=Qry(Div1);
		Div1.pop_back();
		if(q2<=q1){
			Div(Div1,added);
		}
		else{
			Div(Div2,added);
		}
	}else Div(Div2,added);
}

vector<int> ers(vector<int> s,int t){
	vector<int> q;
	for(size_t i=0;i<s.size();i++){
		if(s[i]!=t)q.push_back(s[i]);
	}return q;
}

void Solve(int N) {
	for(int i=0;i<=N*2;i++){
		st[i].clear();
		in[i]=0;
	}
	int csz=0;
	while(csz<N*2){
		vector<int> cur;
		int c = 0;
		vector<int> v2;
		for(int i=1;i<=N*2;i++)if(!in[i]){
			cur.push_back(i);
			int q=Qry(cur);
			if(q<=c){
				cur.pop_back();
				v2.push_back(i);
			}
			else{
				c=q;
				in[i]=1;
				csz++;
			}
		}
		for(size_t i=0;i<v2.size();i++){
			vector<int> v3=cur;
			while(1){
				int sz=v3.size();
				v3.push_back(v2[i]);
				int q=Qry(v3);
				v3.pop_back();
				if(q<=sz){
					Div(v3,v2[i]);
					v3=ers(v3,st[v2[i]].back());
				}else break;
			}
		}
	}
	for(int i=1;i<=N*2;i++)in[i]=0;
	for(int i=1;i<=N*2;i++)for(int j=1;j<=N*2;j++)c[i][j]=0;
	for(int i=1;i<=N*2;i++){
		if(st[i].size()>2){
			assert(st[i].size()==3);
			int sz=st[i].size();
			int t=0;
			for(int k=0;k<sz;k++)for(int k2=k+1;k2<sz;k2++){
				++t;
				vector<int> sq;
				sq.push_back(i),sq.push_back(st[i][k]),sq.push_back(st[i][k2]);
				if(t==3||Qry(sq)==1){
					for(int t=1;t<=2;t++)c[i][sq[t]]=1;
					goto END;
				}
			}
			END:;
		}else{
			for(size_t j=0;j<st[i].size();j++){
				c[i][st[i][j]]=1;
			}
		}
	}
	for(int i=1;i<=N*2;i++){
		for(int j=1;j<=N*2;j++){
			if(!in[i]&&!in[j]&&c[i][j]&&c[j][i]){
				Answer(i,j);
				in[i]=in[j]=1;
			}
		}
	}
	return ;
}

T2

启发式合并即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
const int M = 3e5+5;
typedef long long ll;
typedef pair<int,int> pi;
int sz[N],fa[N];
inline int find(int x){return fa[x]==x?fa[x]:fa[x]=find(fa[x]);}
int n,m;
vector<pi> stk;
map<int,set<int> > Eout[N];
map<int,set<int> > Ein[N];

ll ans = 0;
int Ot[N],In[N];

inline void merge(int u,int v){
	int sz1=In[u]+Ot[u], sz2 = In[v]+Ot[v];
	if(sz1>sz2)swap(sz1,sz2),swap(u,v);
	fa[u]=v;
	for(map<int,set<int> >::iterator it=Eout[u].begin();it!=Eout[u].end();it++){
		for(set<int>::iterator j=it->second.begin();j!=it->second.end();j++){
			int _u=*j;
			int to=it->first;
			Ein[to][u].erase(_u);
			In[to]--;
			if(Ein[to][u].size()==0){Ein[to].erase(u);}
			ans-=sz[to];
			stk.push_back(pi(_u,to));
		}
	}
	for(map<int,set<int> >::iterator it=Ein[u].begin();it!=Ein[u].end();it++){
		for(set<int>::iterator j=it->second.begin();j!=it->second.end();j++){
			int _u=*j;
			int to=it->first;
			Eout[to][u].erase(_u);
			Ot[to]--;
			if(Eout[to][u].size()==0){Eout[to].erase(u);}
			ans-=sz[u];
			stk.push_back(pi(_u,u));
		}
	}
	ans-=1ll*sz[u]*(sz[u]-1);
	ans-=1ll*sz[v]*(sz[v]-1);
	ans+=1ll*sz[u]*In[v];
	sz[v]+=sz[u];
	ans+=1ll*sz[v]*(sz[v]-1);
}

inline void adde(int u,int v){
	int fu=find(u), fv=find(v);
	if(fu==find(v))return ;
	if(Eout[fu].find(fv)!=Eout[fu].end()&&Eout[fu][fv].find(u)!=Eout[fu][fv].end())
    	{return ;}
	Eout[fu][fv].insert(u);
	Ein[fv][fu].insert(u);
	Ot[fu]++, In[fv]++;
	ans+=sz[fv];
	if(Eout[fv].find(fu)!=Eout[fv].end()&&Eout[fv][fu].size())
    	{merge(fu,fv);}
}

int main()
{
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		fa[i]=i,sz[i]=1;
	}
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		stk.push_back(pi(u,v));
		while(stk.size()){
			pi d = stk.back();
			stk.pop_back();
			adde(d.first,d.second);
		}
		printf("%lld\n",ans);
	}
}

T3

判断的做法是从后向前,每次找一个最大的没有被占领的,小于等于 \(w_i\) 的位置(如果没有则最后成为了0)

把两个数字相等看成两个不同的数,最后除 \(2^n\) 即可。

\(g[i][j]\) 表示 \(i\) 个数,\(1\)\(j\) 已经被占领了,满足前 \(i\) 个没有最后成为了 \(0\) 的方案数。

转移用 \(g[k-1][k-1]\) 就行了。(具体可以看代码)

\(f[i][j]\) 即以上的东西加上每个位置是否成为了 \(0\) 的限制。转移是类似的。

#include<bits/stdc++.h>
using namespace std;
const int N = 610;
typedef long long ll;
const int mod = 1e9+7;
inline int add(int a,int b){a+=b;return a>=mod?a-mod:a;}
inline int sub(int a,int b){a-=b;return a<0?a+mod:a;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
inline int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
/* math */
int g[N][N],h[N],n;
int f[N<<1][N],a[N];
int bin[N<<1][N<<1];

int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	reverse(a+1,a+n+1);
	bin[0][0]=1;for(int i=1;i<=n;i++){
		bin[i][0]=1;for(int j=1;j<=i;j++)bin[i][j]=add(bin[i-1][j],bin[i-1][j-1]);
	}
	g[0][0]=1;
	h[0]=1;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=i;j++){
			g[i][j]=add(g[i][j], g[i-1][j]);
			for(int k=1;k+j<=i;k++){
				g[i][j+k] = add(g[i][j+k], mul(g[i-1][j], mul(bin[i-1-j][k-1], mul(h[k-1],1+k))));
			}
		}
		h[i] = g[i][i];
	}
	f[0][0]=1;
	a[0]=n*2+1;
	for(int i=1;i<=n+1;i++){
		int d = a[i-1]-a[i]-1;
		for(int j=0;j<i;j++){
			int h=j-(n*2-a[i-1]-(i-2));
			for(int q=0;q<d;q++){
				f[i-1][j]=mul(f[i-1][j], h-q);
			}
		}
		if(i<=n)
		for(int j=0;j<=i;j++){
			f[i][j]=add(f[i][j], f[i-1][j]);
			for(int k=1;k+j<=i;k++){
				f[i][j+k] = add(f[i][j+k], mul(f[i-1][j], mul(bin[i-1-j][k-1], mul(h[k-1],1+k))));
			}
		}
	}
	int inv2 = (mod+1)>>1;
	int ans = f[n][n];
	for(int i=1;i<=n;i++)ans=mul(ans,inv2);
	cout << ans << endl;
}
posted @ 2020-03-24 20:18  jerome_wei  阅读(383)  评论(0编辑  收藏  举报