Lg 8月赛(构造+交互)

Div2

得分 \(270pts' = 100 + 100 + 0 +70\)

T1「PMOI-4」人赢

打表找规律,大概是每 6 个一个周期。

正经做法是用矩阵加速维护指数,同时用扩展欧拉定理处理模数

#include<bits/stdc++.h>

using namespace std;

#define int long long

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

int a[2333333];
//int a1[23333],a2[23333];
int n,m,k;

signed  main()
{
	read(n);
	read(m);
	read(k);
	a[1]= n;
	a[2] = m;
	for(int i=3;i<=100;i++) a[i] = a[i-1] * a[i-2] % 10;//,cout<<a[i]<<" ";
	
	int op = k;
	k-= 2;
	if(k <= 6) printf("%lld\n",a[2 + k]);
	else
	{
		k = k % 6;
		if(!k) k = 6;
		printf("%lld\n",a[2 + k]);		
	}
//	printf("%lld",a[op]);

 }

T2「PMOI-4」可怜的团主

贪心就行,用 deque 来维护。(还重构了一次

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define pb push_back

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

const int INF = 1e18;
const int np = 1e5 + 5;
int a[np];
int b1[np],b2[np],b3[np],b4[np];
int t1,t2,t3,t4;
deque<int> fu[2],zh[2];

inline int Abs(int x){return x < 0?-x:x;}

inline bool cmp(int a,int b)
{
	return Abs(a) > Abs(b);
}
signed  main()
{
	int n;
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	sort(a + 1,a + 1 + n);
	for(int i=1;i<=n;i++)
	{
		if(a[i] < 0)
		{
			int op = Abs(a[i]);
			if(op & 1) b1[++t1] = a[i];//fu[1].pb(a[i]);
			else b2[++t2] = a[i];//fu[0].pb(a[i]);
		}
		else
		{
			int op = Abs(a[i]);
			if(op & 1) b3[++t3] = a[i];//zh[1].pb(a[i]);
			else b4[++t4] = a[i];//zh[0].pb(a[i]);
		}
	}
	
	sort(b1 + 1,b1 + 1 + t1,cmp);//1
	for(int i=1;i<=t1;i++) fu[1].pb(b1[i]);
	sort(b2 + 1,b2 + 1 + t2,cmp);//2
	for(int i=1;i<=t2;i++) fu[0].pb(b2[i]);
	sort(b3 + 1,b3 + 1 + t3,cmp);//3
	for(int i=1;i<=t3;i++) zh[1].pb(b3[i]);
	sort(b4 + 1,b4 + 1 + t4,cmp);//4
	for(int i=1;i<=t4;i++) zh[0].pb(b4[i]);
	
	int Ans = 0;
	
	for(int i=1;i<=n;i++)
	{
		if(i & 1)
		{
			int op1 = zh[0].size() ? zh[0].front():-INF;
			int op2 = fu[1].size() ? fu[1].front():-INF;
			if(op1 == -INF && op2 == -INF)
			{
				op1 = zh[1].size() ? zh[1].back():-INF;
				op2 = fu[0].size() ? fu[0].back():-INF;				
				if((op1 != -INF && op2 != -INF && op1 < Abs(op2)) ||(op1!=-INF && op2 == -INF))
				{
					Ans += op1;
					Ans -= (n-i) * op1;
					zh[1].pop_back();
				}
				if((op1!=-INF&&op2!=-INF&&op1 > Abs(op2))||(op1==-INF && op2!=-INF))
				{
					Ans += op2;
					Ans += (n-i) * op2;
					fu[0].pop_back();
				}
				continue;
			}
			if((op1 != -INF && op2 != -INF && op1 > Abs(op2)) ||(op1!=-INF && op2 == -INF))
			{
				Ans += op1;
				Ans += (n-i) * op1;
				zh[0].pop_front();
			}
			if((op1!=-INF&&op2!=-INF&&op1 < Abs(op2))||(op1==-INF && op2!=-INF))
			{
				Ans += op2;
				Ans += (n-i) * Abs(op2);
				fu[1].pop_front();
			}
		}
		else
		{
			int op1 = zh[1].size() ? zh[1].front():-INF;
			int op2 = fu[0].size() ? fu[0].front():-INF;
			if(op1 == -INF && op2 == -INF)
			{
				op1 = zh[0].size() ? zh[0].back():-INF;
				op2 = fu[1].size() ? fu[1].back():-INF;				
				if((op1 != -INF && op2 != -INF && op1 < Abs(op2)) ||(op1!=-INF && op2 == -INF))
				{
					Ans += op1;
					Ans -= (n-i) * op1;
					zh[0].pop_back();
				}
				if((op1!=-INF&&op2!=-INF&&op1 > Abs(op2))||(op1==-INF && op2!=-INF))
				{
					Ans += op2;
					Ans += (n-i) * op2;
					fu[1].pop_back();
				}
				continue;				
			}
			if((op1 != -INF && op2 != -INF && op1 > Abs(op2)) ||(op1!=-INF && op2 == -INF))
			{
				Ans += op1;
				Ans += (n-i) * op1;
				zh[1].pop_front();
			}
			if((op1!=-INF&&op2!=-INF&&op1 < Abs(op2))||(op1==-INF && op2!=-INF))
			{
				Ans += op2;
				Ans += (n-i) * Abs(op2);
				fu[0].pop_front();
			}			
		}
	}

	printf("%lld",Ans);
 }

T3「PMOI-4」可怜的团主
太菜了,图上二选一都没做过。

这里给出做法:

首先对原图建出 DFS 树,然后将叶子结点取出。记叶子结点个数为 \(L\),如果 \(L \geq \lfloor\frac{n}{3}\rfloor\),直接取出叶子作为独立集。

若小于,则先在叶子集合中加入根节点,若\(L+1 \mod 2=1\),则再加入一次根节点。

然后按 \(dfn\) 序排序,将 \(i\) 和 \(mid + i\) 进行配对,形成不超过 \(\lceil\frac{n}{6}\rceil\) 条路径

证明正确性:

考虑一个点,若叶子全在它的子树内,那么总会有某个点和根配对,从而导致这个点被覆盖。

若只有一部分叶子在它的子树内,那么一定存在一些叶子和子树外面的叶子匹配,从而导致这个点被覆盖。

综上所述,所有的点都会被覆盖。

#include<bits/stdc++.h>

using namespace std;
template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}
const int np = 1e3 + 5;
const int mp = 1e6 + 5;
int n,m;
int head[np],ver[mp * 2],nxt[mp * 2],tit;
int vis[np],dfn[np],leaf[np],lim,top;
int qaq[np],dep[np],f[20][np],lg[np],sta[np],arc[np];
inline void add(int x,int y)
{
	ver[++tit] = y;
	nxt[tit] = head[x];
	head[x] = tit;
}

inline void dfs(int x,int ff)
{
	vis[x] = 1,dfn[x] = ++ lim;
	dep[x] = dep[ff] + 1;
	f[0][x] = ff;
	int son(0);
	for(int i=head[x],v;i;i=nxt[i])
	{
		v = ver[i];
		if(vis[v]) continue;
		son++;
		dfs(v,x);
	}
	if(!son) leaf[++top]=x;
}

inline int lca_(int u,int v)
{
	if(dep[u] < dep[v]) swap(u,v);
	int delta = dep[u] - dep[v];
	for(int i=lg[delta];i>=0;i--)
	if(dep[f[i][u]] >= dep[v]) u = f[i][u];
	if(u == v) return u;
	delta = dep[v];
	for(int i=lg[delta];i>=0;i--) 
	if(f[i][u]!=f[i][v]) u = f[i][u],v = f[i][v];
	return f[0][u];
}

inline bool cmp(int a,int b)
{
	return dfn[a] < dfn[b];
}

int qcnt=0;
inline void gets(int op,int ed)
{
	qcnt++;
	int lca = lca_(op,ed);
	int step = 0,arcstep(0);
	while(op != lca) sta[++step] = op,op = f[0][op];
	sta[++step] = lca;
	while(ed != lca) arc[++arcstep] = ed,ed = f[0][ed];
	reverse(arc + 1,arc + 1 + arcstep);
	printf("%d ",step + arcstep);
	for(int i=1;i<=step;i++) printf("%d ",sta[i]);
	for(int i=1;i<=arcstep;i++) printf("%d ",arc[i]);
	printf("\n");
}

signed  main()
{
	read(n);
	read(m);
	for(int i=2;i<=n;i++) lg[i] = lg[i >> 1] + 1;
	for(int i=1,a,b;i<=m;i++)
	{
		read(a);
		read(b);
		add(a,b);
		add(b,a);
	}
	
	dfs(1,0);
	for(int i=1;i<=lg[n];i++)
		for(int j=1;j<=n;j++)
		f[i][j] = f[i-1][f[i-1][j]];
	if(top >= (n/3))
	{
		printf("2\n");
		for(int i=1;i<=(n/3);i++)
		printf("%d ",leaf[i]);
		return 0; 
	}
//	int son(0);
//	for(int i=head[1];i;i=nxt[i]) son++;
//	if(son == 1) leaf[++top] = 1;
	leaf[++top] = 1;
	if(top & 1) leaf[++top] = 1;
	sort(leaf + 1,leaf + 1 + top,cmp);
	
	printf("1\n");
	for(int i=1;i<=top/2;i++)
	{
		int gol = i + top/2;
		gets(leaf[i],leaf[gol]);
	}
	
	for(int i=2;i<=n;i++)
	{
		if(qcnt < (n + 5)/6)
		{
			printf("%d %d\n",1,i);
			qcnt++;
		}
	}
	return 0;
 }

T4「PMOI-4」猜排列

交互题。

我们要求出 \(l\) 到 \(r\) 之间的序列,先二分中点 \(mid\) 递归处理 \(l\) 到 \(mid\)。我们已知 \(mid\) 的位置,可以直接用 \(mid\) 对 \([mid+1,r]\) 这段区间做询问一,然后这样就做完了。

#include<bits/stdc++.h>

using namespace std;

//#define int long long
#define pb push_back

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

const int np = 5e4 + 5;

int a[np];
int bit[20][np];
int xx[np],yy[np];
int aux[np],arc[np];
//vector<>
int n,m1,m2,m3;
inline void solve4()
{
	int l(0);
//		printf("? 4 3 ");
	cout<<"? "<<4<<" ";
	for(int i=1;i<=4;i++)cout<<i<<" ";// printf("%lld ",i);
	cout<<"3"<<endl;
//		printf("4\n");
//		fflush(stdout);
//		read(l);
//		scanf("%lld",&l);
	read(l);
	for(int i=1;i<=l;i++) read(xx[i]),yy[xx[i]]=1;
	int top = 0;
	for(int i=1;i<=n;i++)
	{
		if(yy[i] == 0)aux[++top] = i;
	}
	for(int i=1;i<=2;i++)
	{
		for(int q=1;q<=2;q++)
		{
			int ams;
			printf("! %d %d\n",xx[q],aux[i]);
			fflush(stdout);
//			read(ams);
			scanf("%d",&ams);
			if(ams)
			{
				a[aux[i]] = 2;
				arc[2] = aux[i];
				a[xx[q]] = 3;
				arc[3] = xx[q];
				if(i == 1) a[aux[i + 1]] = 1,arc[1] = aux[i + 1];
				if(i == 2) a[aux[i - 1]] = 1,arc[1] = aux[i - 1];
				if(q == 1) a[xx[q + 1]] = 4,arc[4] = xx[q + 1];
				if(q == 2) a[xx[q - 1]] = 4,arc[4] = xx[q - 1];
				printf("A ");
				for(int j=1;j<=3;j++) printf("%d ",a[j]);
				printf("%d",a[4]);
				fflush(stdout);
				return ;
			}
		}
	}	
}

inline void solve(int dep,int l,int r,vector<int> vec) // [l,r] ºÍÕâ¸öÇø¼äÄÚµÄÊý 
{
	int mid = l + r >> 1,lim(0);
	vector<int> nxt;
	if(r-l+1==4) 
	{
		printf("? 4 ");
		int op = -1;
		for(auto i:vec) printf("%d ",i),bit[dep][i] += 1;
		printf("3\n");
		fflush(stdout);
		read(l);
		for(int i=0;i<l;i++) read(aux[i]),bit[dep][aux[i]] += 1;
		for(int i=1;i<=n;i++) if(bit[dep][i] == 1) xx[++op] = i;
		for(int i=0;i<=op;i++)
		{
			for(int j=0,qans;j<l;j++)
			{
				printf("! %d %d\n",aux[j],xx[i]);
				fflush(stdout);
				read(qans);
				if(qans)
				{
					a[xx[i]] = 2;
					a[aux[j]] = 3;
					arc[2] = xx[i];
					arc[3] = aux[j];
					a[xx[i ^ 1]] = 1;
					arc[1] = xx[i ^ 1];
					a[aux[j ^ 1]] = 4;
					arc[4] = aux[j ^ 1];
					return;
				}
			}
		}
	}
	if(l == r)
	{
		for(auto i:vec)a[i] = l,arc[l] = i;
		return;
	}
	printf("? %d ",r-l+1);
	for(auto i:vec) printf("%d ",i),bit[dep][i] = 1;
	printf("%d\n",mid + 1);
	fflush(stdout);
	read(lim);
	for(int i=1,x;i<=lim;i++) read(x),bit[dep][x] += 1;
	for(int i=1;i<=n;i++) if(bit[dep][i] == 1) nxt.pb(i);
	solve(dep + 1,l,mid,nxt);
	for(int i=1,qans;i<=n;i++)
	{
		if(bit[dep][i] == 2)
		{
			printf("! %d %d\n",i,arc[mid]);//ax > ay
			fflush(stdout);
			read(qans);
			if(!qans) a[i] = 2 * mid;
			else a[i] = mid + qans;
			arc[a[i]] = i;
		}
	}
	return ;
}

inline void kkk(int l,int r)
{
	printf("%d %d\n",l,r);
	if(l == r) return;
	int mid = l + r >> 1;
	kkk(l,mid);
	
}



signed  main()
{
	
//	kkk(1,5e2);
	scanf("%d%d%d%d",&n,&m1,&m2,&m3);
	if(n == 4)
	{
		solve4();
		return 0;
	 } 
	vector<int> q;
	for(int i=1;i<=n;i++) q.pb(i);
	solve(1,1,n,q);
	printf("A ");
	for(int i=1;i<=n;i++)
	printf("%d ",a[i]);
	fflush(stdout);	
 }

补完一场 div2。

上一篇:META-INF目录的用处


下一篇:numpy读取本地数据和索引