NOIP模擬57

Varuxn 發表於 2021-09-20

前言

一整套都是水題(儘管 T4 稍有難度。。

從各位的分數上就可以看出來。。Max 的 T1 打掛了,不然就有人 AK 了。。

感覺還好,最後還有 1h 看了看 T4 ,感覺有一點思路,就瞎 jb 胡了上去,樣例一過想都沒想直接走了。。

最後發現和正解的複雜度其實差了一個 n ,思路大體上是對的,但是題目有關於 \(b(S)\) 的定義審錯了,也不知道要取較大的 k 。。。

比較爽的就是拿下了兩個首切(儘管是水題。。

然後在聯考的官網上的排名就比較靠後了,大概到了 26 名,不得不說 cqbz 的人們太強了%%%

NOIP模擬57

NOIP模擬57

T1 2A

解題思路

大水題。。大概學過字串的都能切掉。

我感覺我的做法是比較好寫的,無論讀入的字元是否合法,我們都把它處理成合法的,然後和原串判斷是否相等就好了。

兩個字串判等可以用 string 畢竟我比較懶。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
bool pd(char ch){return ch>='0'&&ch<='9';}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!pd(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(pd(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=50;
int n,cnt,tot,top,sta[N],s[N];
char ch[N];
string s1,s2;
signed main()
{
	freopen("ip.in","r",stdin); freopen("ip.out","w",stdout);
	scanf("%s",ch+1); n=strlen(ch+1);
	for(int i=1;i<=n;i++)
	{
		if(pd(ch[i])&&!pd(ch[i-1])) cnt++;
		if(pd(ch[i])) s[cnt]=(s[cnt]<<1)+(s[cnt]<<3)+(ch[i]^48);
		s1.push_back(ch[i]);
	}
	for(int i=1;i<=cnt;i++) s[i]=min(s[i],255ll);
	for(int i=1;i<=cnt;i++)
	{
		int x=s[i];top=0;
		if(!x) sta[++top]=0;
		while(x) sta[++top]=x%10,x/=10;
		for(int j=top;j>=1;j--) s2.push_back((char)(sta[j]+'0'));
		if(i!=cnt) s2.push_back('.');
	}
	if(s1==s2) printf("YES\n"),exit(0);
	else printf("NO\n");
	for(int i=1;i<=cnt;i++)
	{
		printf("%lld",s[i]);
		if(i!=cnt) printf(".");
	}
	return 0;
}

T2 2B

解題思路

虧我還打了一個搜尋,拍了那麼多組資料。。

NOIP模擬57

真的,看到這題第一眼不是什麼正解,我看到的是高達 60 pts 的部分分。

然後我就果斷碼起了搜尋,交上去之後賽後發現竟然有 75pts 的高分 !!!。

接著就開始想正解,一開始想到的是 之前考的 ZYB玩字串,後來發現複雜度不太行,又想到了之前考過的 string。

記憶化XIN_Team+某些玄學操作???最後發現這不就是一個 括號匹配嗎????

於是一個迴圈解決。。。

code

75pts XIN_Team

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline bool pd(char ch){return ch>='0'&&ch<='9';}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!pd(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(pd(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e4+10;
int n,ans,jud;
char ch[N];
bool check(char s[],int len)
{
	for(int i=2;i<=len;i++) if(s[i]=='P') return true;
	return false;
}
void dfs(char s[],int len)
{
	if(!check(s,len)) return ans=min(ans,len),void();
	if(!ans||ans==jud) printf("%lld",ans),exit(0);
	char fb[len+10];
	for(int i=2;i<=len;i++)
		if(s[i]=='P')
		{
			for(int j=1;j<=i-2;j++) fb[j]=s[j];
			for(int j=i+1;j<=len;j++) fb[j-2]=s[j];
			dfs(fb,len-2);
		}
}
signed main()
{
	freopen("apstr.in","r",stdin); freopen("apstr.out","w",stdout);
	scanf("%s",ch+1); ans=n=strlen(ch+1);
	for(int i=1;i<=n;i++) if(ch[i]=='A') jud++;else jud--;
	dfs(ch,n); printf("%lld",ans); return 0;
}

正解

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;int n,ans,res;char ch[N];
signed main()
{
	freopen("apstr.in","r",stdin); freopen("apstr.out","w",stdout);
	scanf("%s",ch+1); n=strlen(ch+1);
	for(int i=n;i>=1;i--) if(ch[i]=='P') res++; else if(res) res--; else ans++;
	ans+=res&1; printf("%d",ans); return 0;
}

T3 2C

解題思路

有一點思維難度,也許就一點。。

發現操作有難度的只是對於所謂菱形情況的處理,因此我們考慮記錄某個點所有的祖先(也就是所有的可以派生當前點的節點)

然後發現直接記錄的時間複雜度好像不太允許,於是我們可以用取並集的 bitset

對於與之前的元素的並集沒有交集的情況可以直接剪枝掉。

複雜度是 \(\mathcal{O}(\dfrac{n^3}{32})\) ,畢竟 bitset 的時間空間都要小一個 32 嘛,然後差不多是正確的複雜度了(儘管正解是 \(\mathcal{O}(n^2logn)\)...

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline bool pd(char ch){return ch>='0'&&ch<='9';}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!pd(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(pd(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e3+10;
int n,all;
bitset<N> bit[N],temp,emp;
string s[N];
unordered_map<string,int> mp;
signed main()
{
	freopen("class.in","r",stdin); freopen("class.out","w",stdout);
	n=read(); mp.insert(make_pair(":",++all)); mp.insert(make_pair(";",++all));
	for(int i=1;i<=n;i++)
	{
		int pos=1; cin>>s[pos]; temp.reset();
		while(s[pos]!=";") pos++,cin>>s[pos];
		if(mp.find(s[1])!=mp.end()){printf("greska\n");continue;}
		for(int j=2;j<=pos;j++)if(mp.find(s[j])==mp.end()){printf("greska\n");goto V;}
		for(int j=3;j<pos;j++)
		{
			int x=mp.find(s[j])->second;
			bitset<N> f=bit[x]&temp; temp|=bit[x];
			if(f==emp) continue;
			for(int k=3;k<j;k++)
			{
				int y=mp.find(s[k])->second;
				if((bit[y]&bit[x])==emp) continue;
				if(bit[y][x]||bit[x][y]) continue;
				printf("greska\n");goto V;
			}
		}
		mp.insert(make_pair(s[1],++all)); printf("ok\n");
		for(int j=3;j<pos;j++) bit[all]|=bit[mp.find(s[j])->second],bit[all][mp.find(s[j])->second]=true;
		V:;
	}
	return 0;
}

T4 2D

解題思路

顯然是將 k 從大到小進行處理,對於每個點向比自己出度小的點的連邊處理一下。

然後並查集維護一下,就好了,這裡排序用 sort 可能會 TLE ,因此選擇桶排或者基數排序。

考場上是有一個差不多的思路的,但是感覺時間也不夠打出來這個題,連優化都沒優化就交了,結果就。。(0pts程式碼

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define File
using namespace std;
inline bool pd(char ch){return ch>='0'&&ch<='9';}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!pd(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(pd(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int MAXN=1e6+10,INF=1e18;
int n,m,N,M,B,ans=-INF,aid,du[MAXN],cnt[MAXN],rk[MAXN],bou[MAXN];
int rec[MAXN],a[MAXN],siz[MAXN],e[MAXN],fa[MAXN],mi[MAXN];
int tot=1,head[MAXN],ver[MAXN<<1],nxt[MAXN<<1];
struct Node{int id,dat;}s[MAXN],p[MAXN];
void add_edge(int x,int y){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
int find(int x){if(fa[x]==x) return x;return fa[x]=find(fa[x]);}
signed main()
{
	#ifdef File
		freopen("kdgraph.in","r",stdin);
		freopen("kdgraph.out","w",stdout);
	#endif
	n=read(); m=read(); M=read(); N=read(); B=read();
	for(int i=1,x,y;i<=m;i++) du[x=read()]++,du[y=read()]++,add_edge(x,y),add_edge(y,x);
	for(int i=1;i<=n;i++) p[i].dat=du[i],p[i].id=i,cnt[p[i].dat]++;
	for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1],siz[i]=1,e[i]=du[i],fa[i]=i;
	for(int i=n;i>=1;i--) s[rk[i]=cnt[p[i].dat]--]=p[i];
	for(int i=1;i<n;i++) if(s[i].dat!=s[i+1].dat) bou[s[i].dat]=i;
    for(int i=1;i<=n;i++)
		for(int j=head[s[i].id];j;j=nxt[j])
		{
			int to=ver[j];
			if(s[rk[to]].dat<=s[i].dat) continue;
			int pos=bou[--s[rk[to]].dat]+1,id=s[pos].id;
			bou[s[rk[to]].dat]++;
			swap(s[rk[to]],s[rk[id]]); swap(rk[to],rk[id]);
		}
	for(int i=1;i<=n;i++) rec[s[i].id]=s[i].dat;
	memset(rk,0,sizeof(rk)); memset(cnt,0,sizeof(cnt));
	for(int i=1;i<=n;i++) cnt[rec[i]]++;
	for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--) a[rk[i]=cnt[rec[i]]--]=i;
	for(int i=n;i>=0;i--)
	{
		int x=a[i],fat=find(a[i]);
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],fto=find(to);
			if(rk[to]<rk[x]) continue;
			if(fto==fat){mi[fat]++;continue;}
			if(siz[fat]<=siz[fto]) swap(fat,fto);
			siz[fat]+=siz[fto]; mi[fat]+=mi[fto]; e[fat]+=e[fto]; fa[fto]=fat; mi[fat]++;
		}
		if(!i||rec[x]==rec[a[i-1]]) continue;
		for(int j=i;j<=n;j++)
			if(rec[a[j]]==rec[x])
			{
				int fa2=find(a[j]);
				int temp=M*mi[fa2]-N*siz[fa2]+B*(e[fa2]-mi[fa2]*2);
				if(temp>ans) ans=temp,aid=rec[x];
			}
			else break;
	}
	printf("%lld %lld",aid,ans); return 0;
}