2021NOI同步赛

\(NOI\) 网上同步赛

明白了身为菜鸡的自己和普通人的差距


DAY1

\(T1\) 轻重边

【题目描述】

小 W 有一棵 \(n\) 个结点的树,树上的每一条边可能是轻边或者重边。接下来你需要对树进行 \(m\) 次操作,在所有操作开始前,树上所有边都是轻边。操作有以下两种:

  1. 给定两个点 \(a\)\(b\),首先对于 \(a\)\(b\) 路径上的所有点 xx(包含 \(a\)\(b\) ),你要将与 xx 相连的所有边变为轻边。然后再将 \(a\)\(b\) 路径上包含的所有边变为重边。

  2. 给定两个点 \(a\)\(b\) ,你需要计算当前 \(a\)\(b\) 的路径上一共包含多少条重边。


撞题牛客付费题

不会正解。打了30分的暴力和20分的性质A,性质A是一条链,所以用线段树。

代码:
#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int N=1e5+5;
const int M=2e5+10;
int T,n,m;
struct edge{
	int v,w,next;
}e[M];
int head[N],en=1;
void insert(int u,int v){
	e[++en].v=v;
	e[en].w=0;
	e[en].next=head[u];
	head[u]=en;
}
int fa[N][20],dep[N];
void dfs(int u,int f){
	fa[u][0]=f;dep[u]=dep[f]+1;
	for(int i=1;i<20;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].v;
		if(v==fa[u][0])continue;
		dfs(v,u);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=19;i>=0;i--)
		if(dep[fa[x][i]]>=dep[y])
			x=fa[x][i];
	if(x==y)return y;
	for(int i=19;i>=0;i--)
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
int gin[N];
int ntol[N],lton[N];
void dfsA(int u,int f){
	ntol[u]=f;
	lton[f]=u;
	for(int i=head[u];i;i=e[i].next){
		if(e[i].v==lton[f-1])continue;
		dfsA(e[i].v,f+1);
	}
}
//SGt---
int sum[4*N];
int tag[4*N];
void g(int l,int r,int p,int k){
	tag[p]=k;
	sum[p]=k*(r-l+1);
}
void pushdown(int l,int r,int p){
	if(tag[p]){
		int mid=(l+r)>>1;
		g(l,mid,p<<1,tag[p]);
		g(mid+1,r,p<<1|1,tag[p]);
		tag[p]=0;
	}
}
void pushup(int p){sum[p]=sum[p<<1]+sum[p<<1|1];}
void change(int l,int r,int p,int cl,int cr,int d){
	if(cl>cr)return ;
	if(l>=cl&&r<=cr){g(l,r,p,d);return ;}
	pushdown(l,r,p);
	int mid=(l+r)>>1;
	if(cl<=mid)change(l,mid,p<<1,cl,cr,d);
	if(cr>mid)change(mid+1,r,p<<1|1,cl,cr,d);
	pushup(p);
}
int query(int l,int r,int p,int ql,int qr){
	if(l>=ql&&r<=qr)return sum[p];
	pushdown(l,r,p);
	int mid=(l+r)>>1,res=0;
	if(ql<=mid)res+=query(l,mid,p<<1,ql,qr);
	if(qr>mid)res+=query(mid+1,r,p<<1|1,ql,qr);
	return res;
}
//------
inline void clean(){
	memset(fa,0,sizeof(fa));
	memset(head,0,sizeof(head));
	memset(gin,0,sizeof(gin));
	memset(sum,0,sizeof(sum));
	memset(tag,0,sizeof(tag));
	en=1;
}
int main(){
	freopen("edge.in","r",stdin);
	freopen("edge.out","w",stdout);
	T=in;
	while(T--){
		clean();
		n=in,m=in;
		if(n<=5000&&m<=5000){
			for(int i=1;i<n;i++){
				int u=in,v=in;
				insert(u,v);
				insert(v,u);
			}
			dfs(1,0);
			for(int i=1;i<=m;i++){
				int opt=in,a=in,b=in,c=lca(a,b);
				if(opt==1){
					int ta=a,tb=b;
					while(a!=c){
						for(int i=head[a];i;i=e[i].next)
							if(e[i].v==fa[a][0]||e[i].v==ta)e[i].w=e[i^1].w=1;
							else e[i].w=e[i^1].w=0;	
						ta=a,a=fa[a][0];
					}
					while(b!=c){
						for(int i=head[b];i;i=e[i].next)
							if(e[i].v==fa[b][0]||e[i].v==tb)e[i].w=e[i^1].w=1;
							else e[i].w=e[i^1].w=0;
						tb=b,b=fa[b][0];
					}
					for(int i=head[c];i;i=e[i].next)
						if(e[i].v!=ta&&e[i].v!=tb)e[i].w=e[i^1].w=0;
				}
				else{
					int ans=0;
					while(a!=c){
						for(int i=head[a];i;i=e[i].next)
							if(e[i].v==fa[a][0]){
								ans+=e[i].w;
								break;
							}
						a=fa[a][0];
					}
					while(b!=c){
						for(int i=head[b];i;i=e[i].next)
							if(e[i].v==fa[b][0]){
								ans+=e[i].w;
								break;
							}
						b=fa[b][0];
					}
					printf("%d\n",ans);
				}
			}			
		}
		else{
			for(int i=1;i<n;i++){
				int u=in,v=in;
				insert(u,v);
				insert(v,u);
				gin[u]++,gin[v]++;
			}		
			int s;
			for(int i=1;i<=n;i++)
				if(gin[i]==1)s=i;	
			dfsA(s,1);
			for(int i=1;i<=m;i++){
				int opt=in,a=in,b=in;
				if(ntol[a]>ntol[b])swap(a,b);
				a=ntol[a],b=ntol[b];
				if(opt==1){
					change(1,n,1,max(1,a-1),min(n-1,b),0);
					change(1,n,1,a,b-1,1);	
				}
				else{
					int ans;
					if(a==b)ans=0;
					else ans=query(1,n,1,a,b-1);
					printf("%d\n",ans);
				}
			}		
		}
	}
	return 0;
}

\(T2\) 路径交点

【题意翻译】

给定 \(n\) 个点 \(m\) 条边的有向无环图,其中没有入度的点被视为源点,没有出度的点被视为汇点,保证源点和汇点数目相同。

考虑所有把源汇点两两配对,并用两两不相交的路径把它们两两连接起来的所有方案。 如果这个方案中,把源点按标号1到n排序后,得到的对应汇点序列的逆序数对的个数是奇数,那么A给B一块钱,否则B给A一块钱。

问最后A的收益,对大质数取模。


撞题CF167E...现在出题都太卷了

正解是线代行列式,不会。

打了玄学暴力,期望10分。

代码:
#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
const int mod=998244353;
const int N=2e5+5;
const int M=4e6+5;
int T,k;
int n[105];
int m[105];
struct edge{
	int v,next;
}e[M];
int head[N],en=1;
void insert(int u,int v){
	e[++en].v=v;
	e[en].next=head[u];
	head[u]=en;
}
int q[205][105];
int ji,ou;
int vis[N];
void dfs(int x,int d,int sum){
	if(x==n[1]&&d==k){
		if(sum%2==1)ji=(ji+1)%mod;
		else ou=(ou+1)%mod;
		return ;
	}
	if(d==k)dfs(x+1,1,sum);
	int u=q[x][d];
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].v;
		if(vis[v])continue;
		int temp=0;
		vis[v]=1;
		q[x][d+1]=v;
		for(int j=1;j<x;j++)
			if((q[x][d]-q[j][d])*(q[x][d+1]-q[j][d+1])<0)temp++;
		dfs(x,d+1,sum+temp);
		vis[v]=0;
	}
}
int main(){
	freopen("xpath.in","r",stdin);
	freopen("xpath.out","w",stdout);
	T=in;
	while(T--){
		memset(head,0,sizeof(head));
		en=ji=ou=0;
		k=in;
		for(int i=1;i<=k;i++)
			n[i]=in;
		for(int i=1;i<k;i++)
			m[i]=in;
		int t=0;
		for(int i=1;i<k;i++){
			for(int j=1;j<=m[i];j++)
				insert(in+t,in+t+n[i]);
			t+=n[i];
		}
		for(int i=1;i<=n[1];i++)
			q[i][1]=i;
		dfs(1,1,0);
		printf("%d\n",(ou-ji+mod)%mod);
	}
	return 0;
}


\(T3\) 庆典

【题目描述】

C 国是一个繁荣昌盛的国家,它由 \(n\) 座城市和 \(m\) 条有向道路组成,城市从 \(1\)\(n\) 编号。如果从 \(x\) 号城市出发,经过若干条道路后能到达 \(y\) 号城市,那么我们称 \(x\) 号城市可到达 \(y\) 号城市,记作 \(x\Rightarrow y\)。C 国的道路有一个特点:对于三座城市 \(x\)\(y\)\(z\),若 \(x\Rightarrow z\)\(y\Rightarrow z\),那么有 \(x\Rightarrow y\)\(y\Rightarrow x\)

再过一个月就是 C 国成立的千年纪念日,所以 C 国的人民正在筹备盛大的游行庆典。目前 C 国得知接下来会有 \(q\) 次游行计划,第 \(i\) 次游行希望从城市 \(s_i\) 出发,经过若干个城市后,在城市 \(t_i\) 结束,且在游行过程中,一个城市可以被经过多次。为了增加游行的乐趣,每次游行还会临时修建出 \(k(0 \le k \le 2)\)条有向道路专门供本次游行使用,即其它游行计划不能通过本次游行修建的道路。

现在 C 国想知道,每次游行计划可能会经过多少座城市

注意:临时修建出的道路可以不满足 C 国道路原有的特点

【输入格式】

第一行包含四个整数 \(n\),\(m\),\(q\),\(k\),分别表示城市数、道路数、游行计划数以及每次游行临时修建的道路数。

接下来 \(m\) 行,每行包含两个整数 \(u\),\(v\),表示一条有向道路 \(u\rightarrow v\)

接下来 q 行,每行前两个整数 \(s_i\),\(t_i\),表示每次游行的起点与终点;这行接下来有 \(k\) 对整数 \(a\),\(b\),每对整数表示一条临时添加的有向道路 \(a\rightarrow b\)

数据保证,将 C 国原有的有向道路视为无向道路后,所有城市可以互达。


不会,神仙题。

想打个缩点+bfs暴力可是写挂了。0分。

代码:
#include<bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
int s,t,n,m,q,k;
const int N=3e5+5;
const int M=6e5+5;
struct edge{
	int v,next;
}e[M];
int head[N],en;
void insert(int u,int v){
	e[++en].v=v;
	e[en].next=head[u];
	head[u]=en;
}
int dfn[N],low[N],df,vis[N],top,sta[N],sum,id[N],idw[N];
void dfs(int x){
	dfn[x]=low[x]=++df;
	sta[++top]=x;vis[x]=1;
	for(int i=head[x];i;i=e[i].next){
		int v=e[i].v;
		if(!dfn[v]){
			dfs(v);
			low[x]=min(low[x],low[v]);
		}
		else if(vis[v]) low[x]=min(low[x],dfn[v]);
	}
	if(dfn[x]==low[x]){
		int i=sta[top];
		sum++;
		idw[sum]=1;
		while(i!=x){
			idw[sum]++;
			id[i]=sum;
			vis[i]=0;
			i=sta[--top];
		}
		id[i]=sum;
		vis[i]=0;
		--top;
	}
}
struct Edge{
	int v,next;
}E[M];
int Head[N],En;
void Insert(int u,int v){
	E[++En].v=v;
	E[En].next=Head[u];
	Head[u]=En;
}
int que[90000005];
int pre[90000005];
int qh=0,qt=0;
int ans;
int bfs(int s,int t){
	int res=0;
	qh=qt=0;
	que[++qt]=s,pre[qt]=0;
	while(qh<qt){
		qh++;
		int u=que[qh];
		if(u==t){
			int now=qh;
			while(pre[now]!=0){
				res+=idw[que[now]];
				idw[que[now]]=0;
				now=pre[now];
			}
			res+=idw[que[now]];
			idw[que[now]]=0;			
		}
		for(int i=Head[u];i;i=E[i].next){
			int v=e[i].v;
			que[++qt]=v,pre[qt]=qh;
		}
	}
	return res;
}
inline void clean(){
	memset(Head,0,sizeof(Head));
	memset(vis,0,sizeof(vis));
	memset(idw,0,sizeof(idw));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	En=ans=df=sum=0;
}
int temp[5];
void del(int u){
	head[u]=e[en].next;
	en--;
}
int main(){
	freopen("celebration.in","r",stdin);
	freopen("celebration.out","w",stdout);
	n=in,m=in,q=in,k=in;
	for(int i=1;i<=m;i++)
		insert(in,in);
	for(int i=1;i<=q;i++){
		clean();
		s=in,t=in;
		for(int i=1;i<=k;i++){
			temp[i]=in;
			insert(temp[i],in);
		}
		for(int i=1;i<=n;i++)
			if(!dfn[i])dfs(i);
		for(int i=1;i<=n;i++)
			for(int j=head[i];j;j=e[j].next)
				if(id[i]!=id[e[j].v])Insert(id[i],id[e[j].v]);
		s=id[s],t=id[t];
		printf("%d\n",bfs(s,t));
		for(int i=1;i<=k;i++)
			del(temp[i]);	
	}
	return 0;
}

T1线段树写挂了,以后tag全部清-1,不能清0...
T2暴力成功。
T3不出预料地抱铃。
day1=30+30+0=60


DAY2

T1 量子通信

暴力。好多人用随机算法,不会。。。

代码:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull; 
//题意putin------------------------------------------- 
const int N = 400000;
bool s[N+1][256];
ull myRand(ull &k1, ull &k2) {
    ull k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= (k3 << 23);
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}

void gen(int n, ull a1, ull a2) {
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 256; j++)
            s[i][j] = (myRand(a1, a2) & (1ull << 32)) ? 1 : 0;
}
//-----------------------------------------------------
#define in read() 
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c<='9'&&c>='0'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
int n,m;
ull a1,a2;
bool a[256];
int ans;
signed main(){
	freopen("qi.in","r",stdin);
	freopen("qi.out","w",stdout);
	cin>>n>>m>>a1>>a2;
	gen(n,a1,a2);
	for(int g=1;g<=m;g++){
		string c;
		int k,flag=0;
		cin>>c>>k;
		for(int i=0;i<64;i++){
			int t;
			if(c[i]>'9'||c[i]<'0')t=c[i]-'A'+10;
			else t=c[i]-'0';
			for(int j=3;j>=0;j--){
				a[i*4+j]=(t%2)^ans;
				t/=2;
			}
		}
		for(int i=1;i<=n;i++){
			int t=0;
			for(int j=0;j<256;j++){
				if(a[j]!=s[i][j])t++;
				if(t>k)break;
			}
			if(t<=k)flag=1;
		}
		ans=flag;
		printf("%d\n",ans);
	}
	return 0;
}

T2 密码箱

打了一个平衡树,调了半场比赛,还写挂了?

矩阵是真想不到。

代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=200005;
const int mod=998244353;
inline int read(){
	int p=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
	while(c<='9'&&c>='0'){p=p*10+c-'0';c=getchar();}
	return p*f;
}
struct node{
	int v;
	int fa;
	int ch[2];
	int siz;
	int rev;
	int filp;
}a[maxn];
int rt,tot,n,m;
int w[maxn];
inline void pushup(int x){
	a[x].siz=a[a[x].ch[0]].siz+a[a[x].ch[1]].siz+1;
}
inline void pushdown(int x){
	if(a[x].rev){
		a[x].rev=0;
		a[a[x].ch[0]].rev^=1;
		a[a[x].ch[1]].rev^=1;
		swap(a[x].ch[0],a[x].ch[1]);
	}
	if(a[x].filp){
		a[x].filp=0;
		if(a[x].v)a[x].v=3-a[x].v;
		a[a[x].ch[0]].filp^=1;
		a[a[x].ch[1]].filp^=1;
	}
}
inline void rotate(int x){
	int y=a[x].fa,z=a[y].fa,d=(a[y].ch[1]==x);
	pushdown(y);pushdown(x);
	a[y].ch[d]=a[x].ch[d^1];
	a[a[y].ch[d]].fa=y;
	a[x].ch[d^1]=y;a[y].fa=x;
	a[x].fa=z;
	if(z){
		if(y==a[z].ch[1])a[z].ch[1]=x;
		else a[z].ch[0]=x;
	}
	pushup(y);
	pushup(x);
}
inline void splay(int x,int g){
	for(;a[x].fa!=g;rotate(x))
		if(a[a[x].fa].fa!=g) rotate(((x==a[a[x].fa].ch[1])==(a[x].fa==a[a[a[x].fa].fa].ch[1]))?a[x].fa:x);	
	if(g==0)rt=x;
}
inline int newnode(int x,int fa){
	a[++tot].fa=fa;
	a[tot].v=x;
	a[tot].siz=1;
	a[tot].rev=a[tot].filp=0;
	return tot;
}
inline int built(int l,int r,int fa){
	if(l>r)return 0;
	int mid=(l+r)>>1;
	int x=newnode(w[mid],fa);
	a[x].ch[0]=built(l,mid-1,x);
	a[x].ch[1]=built(mid+1,r,x);
	pushup(x);
	return x;
}
inline int find(int x){
	int now=rt;
	while(true){
		pushdown(now);
		if(x==a[a[now].ch[0]].siz+1)return now;
		else if(x<a[a[now].ch[0]].siz+1)now=a[now].ch[0];
		else{x-=a[a[now].ch[0]].siz+1;now=a[now].ch[1];}
	}
}
inline void reverse(int l,int r){
	int pre=find(l-1),nxt=find(r+1);
	splay(pre,0);splay(nxt,pre);
	int now=a[nxt].ch[0];
	a[now].rev^=1;
}
inline void filp(int l,int r){
	int pre=find(l-1),nxt=find(r+1);
	splay(pre,0);splay(nxt,pre);
	int now=a[nxt].ch[0];
	a[now].filp^=1;
}
inline void insert(int x){
	int pre=find(n+1),nxt=find(n+2);
	splay(nxt,0);splay(pre,nxt);
	a[pre].ch[1]=newnode(x,pre);
	pushup(pre),pushup(nxt);
}
int tc,cz[maxn];
inline void out(int p){
	if(!p)return;
	pushdown(p);
	out(a[p].ch[0]);
	if(a[p].v!=0)cz[++tc]=a[p].v;
	out(a[p].ch[1]);
}
int top,q[2*maxn+20];
int mul(int x,int y){return x*y%mod;}
int add(int x,int y){return (x+y)%mod;}
struct fenshu{
	int fz,fm;
};
int gcd(int a,int b){return b?gcd(b,a%b):a;}
fenshu operator+(const fenshu &x,const fenshu &y){
	fenshu z;
	z.fm=mul(x.fm,y.fm);
	z.fz=add(mul(x.fz,y.fm),mul(y.fz,x.fm));
	int t=gcd(z.fm,z.fz);
	z.fm/=t,z.fz/=t;
	return z;
}
void work(){
	top=0;
	q[++top]=0,q[++top]=1;
	for(int i=1;i<=tc;i++){
		if(cz[i]==1)q[top]++;
		else{
			if(q[top]==1)q[top-1]++;
			else q[top]--,q[++top]=1,q[++top]=1;
		}
	}
	tc=0;
	fenshu ans;ans.fm=1,ans.fz=q[top];
	for(int i=top-1;i>=1;i--){
		swap(ans.fm,ans.fz);
		fenshu temp;temp.fm=1;temp.fz=q[i];
		ans=ans+temp;
	}
	cout<<ans.fz<<" "<<ans.fm<<'\n';
	return ;
}
signed main(){
//	freopen("code.in","r",stdin);
//	freopen("code.out","w",stdout); 
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		char c;cin>>c;
		w[i]=(c=='W')?1:2;
	}
	built(0,n+1,0);rt=1;
	out(rt);
	work();
	for(int i=1;i<=m;i++){
		char s[11];
		cin>>s;
		if(s[0]=='A'){char c;cin>>c;insert((c=='W')?1:2);n++;}
		else if(s[0]=='F'){int l=read(),r=read();filp(l+1,r+1);}
		else if(s[0]=='R'){int l=read(),r=read();reverse(l+1,r+1);}
		out(rt);
		work();
	}
	return 0;
}

T3 机器人游戏

把n=1且m=1分析了一下。

小D几毫秒就能做的我都做不来,更不要说小D不会的。

代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
string s[1005];
const int mod=1e9+7;
signed main(){
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>s[i];
	if(s[1][0]=='0'||s[1][0]=='1'||s[1][0]=='*')cout<<3;
	else cout<<1;
	return 0;
}

d2t2平衡树写挂了,以后再不碰Splay。

day2=20+0+8=28


NOI2021=30+30+0+20+0+8=88

写挂的有点多,d1t1写挂20,d1t3多花点时间可能是有 \([16,28]\) 的,d2t2也写挂20。


大概再发挥好点能有个场外Cu。果然我是菜鸡啊。

posted @ 2021-07-26 17:31  llmmkk  阅读(167)  评论(0)    收藏  举报