NOIP模擬77

Varuxn 發表於 2021-10-16

前言

感覺最近太飄了,這次考試是挺好的一次打擊(好像也不算是)。

犯了一個智障錯誤(雙向邊一倍陣列 100pts->30pts)別的就。。

T1 最大或

解題思路

一開始我以為是一個找規律,然而在打表找規律 20min 後無果。。

然後開始考慮二進位制位上的東西,畫了一顆 Tire 樹,發現從高位到低位相同的位我們無論如何或貢獻都是一樣的。。

直到第一個不同的位開始都是可以通過 \(or\) 操作成為 1 的。

然後擴充套件一下,我試了試左右短點在 \([1000,1000]\) 區間內的情況把詢問改成 \(xor\) 也是類似的關係,只不過之前的位都是 0了。

程式碼也就和題目做法差了一句話 for(int i=60;i>=0;i--)if(((l>>i)&1)!=((r>>i)&1)){ans+=(1ll<<i+1)-1;break;}

code

#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int T,l,r,pre,ans;
void solve()
{
	l=read(); r=read(); ans=0;
	for(int i=60;i>=0;i--)
		if(((l>>i)&1)==((r>>i)&1)) ans=ans+(l&(1ll<<i));
		else{ans+=(1ll<<i+1)-1;break;}
	printf("%lld\n",ans);
}
signed main()
{
	freopen("maxor.in","r",stdin); freopen("maxor.out","w",stdout);
	T=read(); while(T--) solve();
	return 0;
}

T2 答題

解題思路

個人感覺這個題的思路較於這個題目本身要重要。

本道題的實質其實是求用所給的 \(n\) 個陣列成的 \(2^n\) 個數中的第 \(\operatorname{ceil}(2^n\times p)\) 大值。

直接列舉顯然不能通過,所以我們需要進行折半列舉(\(meet\;in\;the\;middle\)),即列舉求出前 \(\frac{n}{2}\) 個數可以組成的所有值,以及後 \(\frac{n}{2}\) 個數可以組成的所有值。

二分需要求的最後答案是多少。

對於每一個二分到的值,我們要求出所有組合中小於它的數字個數。

雙指標掃一遍即可。

code

#include <bits/stdc++.h>
#define int long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=3e6+10;
int n,l,r,ans,cnt,t1,t2,s[N],a[N],b[N];
double p,temp;
bool check(int x)
{
	int pos=t2;double sum=0;
	for(int i=1;i<=t1&&a[i]<=x;i++){while(pos>=1&&a[i]+b[pos]>=x) pos--;sum+=pos;}
	return sum<=temp;
}
signed main()
{
	freopen("answer.in","r",stdin); freopen("answer.out","w",stdout);
	n=read(); cnt=n>>1; scanf("%lf",&p); temp=p*(1ll<<n);
	for(int i=1;i<=n;i++) s[i]=read(),r+=s[i];
	for(int sta=0;sta<(1ll<<cnt);sta++){t1++;for(int i=1;i<=cnt;i++)a[t1]+=((sta>>i-1)&1)*s[i];}
	for(int sta=0;sta<(1ll<<n-cnt);sta++){t2++;for(int i=1;i<=n-cnt;i++)b[t2]+=((sta>>i-1)&1)*s[i+cnt];}
	sort(a+1,a+t1+1);sort(b+1,b+t2+1);while(l<=r){int mid=(l+r)>>1;if(check(mid))l=mid+1,ans=mid;else r=mid-1;}
	printf("%lld",ans); return 0;
}

T3 聯合權值?改

解題思路

正解不是特別會,但是 bitset 卡空間卡時間可以跑過。。(建邊陣列開兩倍。。。)

思路就比較簡單了,列舉每一個三元組的中轉點,再列舉連邊判斷兩者之間是否有連邊。

時間複雜度 \(\mathcal{O}((n+m)m)\) 空間複雜度 \(\mathcal{O}(\frac{n^2}{32})\)

code

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=3e4+10;
ll n,m,task,ans1=-1,ans2,s[N];
vector<int> v[N];
bitset<N> e[N];
void add_edge(int x,int y){v[x].push_back(y);}
signed main()
{
	freopen("link.in","r",stdin); freopen("link.out","w",stdout);
	n=read(); m=read(); task=read();
	for(int i=1,x,y;i<=m;i++)
		x=read(),y=read(),e[x][y]=e[y][x]=true,
		add_edge(x,y),add_edge(y,x);
	for(int i=1;i<=n;i++) s[i]=read();
	for(int i=1;i<=n;i++)
		for(int j=0;j<v[i].size();j++)
		{
			int x=v[i][j];
			for(int k=j+1;k<v[i].size();k++)
			{
				int y=v[i][k];
				if(e[x][y]) continue;
				ans1=max(ans1,1ll*s[x]*s[y]);
				ans2+=1ll*s[x]*s[y];
			}
		}
	if(task!=2) printf("%lld\n",ans1); else printf("0\n");
	if(task!=1) printf("%lld",ans2*2); else printf("0");
	return 0;
}

主僕見證了 Hobo 的離別

解題思路

發現把新建的點與之前點的關係當做連邊的話,其實就形成了一棵樹(畢竟題目保證了每個節點最多被融合一次)

那麼我們考慮如何維護,可以對於不同的關係視為不同的邊權。

對於詢問的 \(X,Y\) 之間一定存在著祖先關係,因此可以利用 DFS 序進行判斷。

如果 \(X\)\(Y\) 的祖先那麼著一路的關係都只能是交。

同樣的,如果 \(X\)\(Y\) 的祖先那麼著一路的關係都只能是並。

並茶几維護即可,注意特判 \(K=1\) 也就是兩個完全相同的點的情況。

code

#include <bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=5e5+10;
int n,m,cnt,lab,tim,dfn[N],siz[N],du[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1];
pair<int,int> q[N];
struct DSU
{
	int fa[N];
	void init(){for(int i=1;i<=lab;i++)fa[i]=i;}
	int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}
	void merge(int x,int y){if(find(x)!=find(y)) fa[find(x)]=find(y);}
}T0,T1;//0->&,1->|
void add_edge(int x,int y,int val)
{
	ver[++tot]=y; edge[tot]=val; du[y]++;
	nxt[tot]=head[x]; head[x]=tot;
}
void dfs(int x)
{
	dfn[x]=++tim; siz[x]=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i],val=edge[i]; dfs(to); siz[x]+=siz[to];
		if(val!=1) T0.merge(x,to); if(val) T1.merge(x,to);
	}
}
bool judge(int x,int y){return dfn[x]<=dfn[y]&&dfn[x]+siz[x]-1>=dfn[y];}
void solve(int x,int y)
{
	if(judge(x,y)&&T0.find(x)==T0.find(y)) return printf("1\n"),void();
	else if(judge(y,x)&&T1.find(x)==T1.find(y)) return printf("1\n"),void();
	else printf("0\n");
}
signed main()
{
	freopen("friendship.in","r",stdin); freopen("friendship.out","w",stdout);
	lab=n=read(); m=read();
	for(int i=1,opt,link,t,x,y;i<=m;i++)
	{
		opt=read();x=y=t=link=0;
		if(!opt)
		{
			link=read();t=read();lab++; if(t==1) link=2;
			for(int j=1;j<=t;j++) x=read(),add_edge(lab,x,link);
		}
		else{x=read();y=read();q[++cnt]=make_pair(x,y);}
	}
	T0.init(); T1.init(); for(int i=1;i<=lab;i++) if(!du[i]) dfs(i);
	for(int i=1;i<=cnt;i++) solve(q[i].first,q[i].second);
	return 0;
}