UPC 2020年春混合个人训练第三十四场(省选模拟)【A&B&C&E&G】

问题 A: 图 (graph)
题目描述
有一张n个点,m条边的无向图,你想选出一个非空点集,使得仅保留这个点集中的点和两个端点都在这个点集里的边后得到的图是连通的。你想知道有多少种可能的选点集的方案。
由于出题人不是毒瘤,所以本题不对998244353取模,改为对2取模。

输入
第一行两个正整数n,m,表示图的点数和边数。
接下来m行,每行两个正整数ai,bi表示一条边。

输出
输出一行一个整数表示答案。

样例输入
3 2
1 2
2 3

样例输出
0

提示
样例解释:答案在取模前为6。 选取的集合分别为{1},{2},{3},{1,2},{2,3},{1,2,3}。
在这里插入图片描述
题解:乍一看不太好做,但是题目要求求 %2之后的结果。不妨考虑 %2的性质,在 %4意义下,只有21%4=2,其他的 %4=0,所以我们把问题转换成求 2子图中联通块个数 %4。如果答案=2那么输出1
,这东西怎么求呢?考虑将子图黑白染色,这样保证一个联通块有两种方案,所以用状压dp即可。

#include<bits/stdc++.h>
using namespace std;
const int o=531446;

int p[55],f[55][o],n,m,ans;
bool bz[55][55];

void add(int &x,int y){
	x+=y; x%=4;
}

int calc(int x,int y){
	return x/p[y]%3;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,x,y; i<=m; i++) scanf("%d%d",&x,&y),bz[x][y]=bz[y][x]=1;
	p[0]=1;
	for(int i=1; i<=12; i++) p[i]=p[i-1]*3;
	f[1][0]=f[1][p[11]]=f[1][2*p[11]]=1;
	for(int i=2; i<=n; i++) for(int s=0; s<p[12]; s++) if(f[i-1][s])
	{
		int v=f[i-1][s],t=s/3;
		add(f[i][t],v);
		bool check1=1,check2=1;
		for(int j=0; j<=11; j++) if(i-12+j>=1&&bz[i][i-12+j])
		{
			int x=calc(s,j);
			if(x==1) check2=0;
			else if(x==2) check1=0;
		}
		if(check1) add(f[i][t+p[11]],v);
		if(check2) add(f[i][t+2*p[11]],v);
	}
	for(int s=0; s<p[12]; s++) add(ans,f[n][s]);
	(ans+=3)%=4;
	if(ans==2) ans=1;
	cout<<ans<<endl;
}

问题 B: 字符串 (string)
题目描述
你喜欢字符串。有人送了你一个仅含小写字母的字符串。
由于你是一名优秀的 OIer,所以你决定对这个字符串展开研究。
定义两个字符串是相似的,当且仅当存在至多一个i,使得这两个字符串中只有第i个字母不同。
你取出了这个字符串中所有长度为m的子串。你想知道,对于每个长度为m的子串,有多少个其它长度为m的子串与它相似。

输入
第一行两个正整数n,m,表示字符串长度和你取出的子串长度。
接下来一行一个长度为n的仅包含小写字母的字符串。

输出
输出一行n-m+1个整数,第i个整数表示有多少个串与第i个串相似(不包括i自身)。

样例输入
8 3
aabaabab

样例输出
2 1 1 2 1 3

提示
对于100%的数据,1≤n≤10^5,1≤m≤n。

题解:
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+77;

struct S{
	int l,r,v;
	
	S(){}
	S(int _l,int _r,int _v) :
		l(_l),r(_r),v(_v) {} 
};

int c[N],d[N],son[N],n,m,ans[N],p1[N],p2[N],L,f[N][18],szeA[N],szeB[N],rt;
int lst_y[N],dfn_l,dfn_r,dfnA[N],dfnB[N],timA,timB,real_ans[N];
char s[N];
vector<S>g[N];
vector<int>h[N];

struct SAM
{
	struct point{
		int go[26];
	}t[N];
	
	int maxl[N],fa[N],tot = 1,lst = 1,id[N],anc[N][18],dep[N];
	vector<int>g[N];
	
	void insert(char s) {
		int c = s - 'a',i = lst;
		lst = ++tot;
		maxl[lst] = maxl[i] + 1;
		for(; i && !t[i].go[c]; i = fa[i]) t[i].go[c] = lst;
		if(!i) fa[lst] = 1;
		else {
			int j = t[i].go[c];
			if(maxl[j] == maxl[i]+1) fa[lst] = j;
			else {
				int p;
				t[p = ++tot] = t[j];
				fa[p] = fa[j];
				fa[j] = fa[lst] = p;
				maxl[p] = maxl[i] + 1;
				for(; i && t[i].go[c] == j; i = fa[i]) t[i].go[c] = p;
			}
		} 
	}
	
	void build() {
		for(int i = 2; i <= tot; i++) g[fa[i]].push_back(i);
	}
	
}A,B;

void change(int *c,int x,int v) {
	for(int i = x; i <= L; i += i & -i) c[i] += v;
}

int query(int *c,int x) {
	int res = 0;
	for(int i = x; i; i -= i & -i) res += c[i];
	return res;
}

void dfsA(int u) {
	dfnA[u] = ++timA;
	szeA[u] = 1;
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) {
		int v = A.g[u][i];
		dfsA(v);
		szeA[u] += szeA[v];
		if(szeA[v] > szeA[son[u]]) son[u] = v;
	}
}

void dfsB(int u) { 
	dfnB[u] = ++timB;
	szeB[u] = 1;
	int len = B.g[u].size(),i;
	for(i = 0; i < 17; i++) f[u][i + 1] = f[f[u][i]][i];
	for(i = 0; i < len; i++) {
		int v = B.g[u][i];
		f[v][0] = u;
		dfsB(v);
		szeB[u] += szeB[v];
	}
}

int jumpB(int x,int d) {
	for(int i = 17; i >= 0; i--)
	if(f[x][i] && B.maxl[f[x][i]] >= d) x = f[x][i];
	return x;
}

void add(int u) {
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(c,dfnB[z],1);
}

void del(int u) {
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(c,dfnB[z],-1);
}

void ask(int u,int x) { 
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos],y = jumpB(z,m - 1 - A.maxl[x]);
	lst_y[u] = y;
	ans[pos] += query(c,dfnB[y] + szeB[y] - 1) - query(c,dfnB[y] - 1);
}

void calc_son(int u) {
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos],y = lst_y[u];
	ans[pos] += query(d,dfnB[y] + szeB[y] - 1) - query(d,dfnB[y] - 1);
}

void ins(int u) {
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(d,dfnB[z],1);
}

void clear(int u) {
	if(!A.id[u]) return;
	int pos = A.id[u],z = p2[pos];
	change(d,dfnB[z],-1);
}

void join(int u) {
	if(!A.id[u]) return;
	int y = lst_y[u];
	g[dfn_l].push_back(S(dfnB[y],dfnB[y] + szeB[y] - 1,1));
	g[dfn_r + 1].push_back(S(dfnB[y],dfnB[y] + szeB[y] - 1,-1));
}

void dfs_add(int u) {
	add(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_add(A.g[u][i]);
}

void dfs_del(int u) {
	del(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_del(A.g[u][i]);
}

void dfs_ask(int u,int x) {
	ask(u,x);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_ask(A.g[u][i],x); 
}

void dfs_cs(int u) {
	calc_son(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_cs(A.g[u][i]); 
}

void dfs_ins(int u) {
	ins(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_ins(A.g[u][i]);
}

void dfs_clear(int u) {
	clear(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_clear(A.g[u][i]);
}

void dfs_join(int u) {
	join(u);
	int len = A.g[u].size(),i;
	for(i = 0; i < len; i++) dfs_join(A.g[u][i]);
}

void dfsC(int u,bool op) { 
	int i,len = A.g[u].size();
	
	for(i = 0; i < len; i++) {
		int v = A.g[u][i];
		if(v != son[u]) dfsC(v,0);
	}
	if(son[u]) dfsC(son[u],1);
	
	ask(u,u); add(u);
	for(i = 0; i < len; i++) {
		int v = A.g[u][i];
		if(v != son[u]) {
			dfs_ask(v,u);
			dfs_add(v);
		}
	}
	del(u);
	for(i=0; i<len; i++) {
		int v = A.g[u][i];
		if(v != son[u]) dfs_del(v);
	}
	
	for(i = len-1; i>=0; i--) {
		int v = A.g[u][i];
		if(v != son[u]) {
			dfs_ask(v,u);
			dfs_add(v);
		}
	}
	ask(u,u);
	for(i=0; i<len; i++) {
		int v = A.g[u][i];
		if(v != son[u]) dfs_del(v);
	}
	
	calc_son(u);
	for(i=0; i<len; i++) {
		int v = A.g[u][i];
		if(v != son[u]) dfs_cs(v);
	}
	if(op) {
		ins(u);
		for(i = 0; i < len; i++) {
			int v = A.g[u][i];
			if(v != son[u]) dfs_ins(v);
		}
	}
	else if(son[u]) dfs_clear(son[u]);
	
	if(son[u]) {
		dfn_l = dfnA[son[u]]; 
		dfn_r = dfn_l + szeA[son[u]] - 1;
		join(u);
		for(i=0; i<len; i++) {
			int v = A.g[u][i];
			if(v != son[u]) dfs_join(v);
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	int i,j;
	scanf("%s",s + 1);
	for(i=1; i<=n; i++) {
		A.insert(s[i]);
		if(i>=m) A.id[A.lst]=i-m+1, p1[i-m+1]=A.lst;
	}
	for(i=n; i >= 1; i--) {
		B.insert(s[i]);
		if(i+m-1 <= n) B.id[B.lst]=i, p2[i]=B.lst;
	}
	A.build();  B.build();  L=B.tot;
	dfsA(1); dfsB(1); dfsC(1,0);
	memset(c,0,sizeof(c));
	for(i=1; i<=n-m+1; i++) h[dfnA[p1[i]]].push_back(i);
	for(i=1; i<=A.tot; i++) {
		int leng = g[i].size();
		for(j=0; j<leng; j++) {
			S tmp = g[i][j];
			change(c,tmp.l,tmp.v);
			change(c,tmp.r + 1,-tmp.v);
		}
		int lenh = h[i].size();
		for(j=0; j<lenh; j++) {
			int x = h[i][j];
			ans[x] += query(c,dfnB[p2[x]]);
		}
	}
	
	for(i=1; i<=n-m+1; i++)	 printf("%d ",ans[i]);
}

问题 C: 楼房搭建 (building)
题目描述
小 H 是一个建筑师,他接到了一个任务 —— 按照计划图搭建一排楼房。计划图上从左到右给出了n个非负整数,对于第i个数hi,它表示在i这个位置搭建出来的楼房的高度不能小于hi。
小 H 搭建楼房的方式也很特别。在每一时刻,它总可以让相邻的两个楼房分别增高1个单位和增高2个单位。具体地,对于任意的i(1≤i<n),每一时刻他可以有以下两种搭建的方法:
1.让i位置上的楼房的高度+2,同时让i+1位置上的楼房+1。
2.让i位置上的楼房的高度+1,同时让i+1位置上的楼房+2。
小 H 想知道最快需要多少时间,搭建出来的这一排楼房才能满足计划图的要求。

输入
第一行一个整数n。 第二行n个整数,分别为h1,h2,…,hn。

输出
一行一个答案,表示最快需要多少时间。

样例输入
【样例1】
4
2 1 1 2
【样例2】
13
12 13 9 8 3 21 7 10 1 1 8 1 1

样例输出
【样例1】
2
【样例2】
35

提示
样例1解释:
最开始四个楼房的高度为0 0 0 0 ,小 H 最少需要如下两次搭建:
1.给第1,2个位置分别+2和+1,高度变为2 1 0 0。
2.给第3,4个位置分别+1和+2,高度变为2 1 1 2。
在这里插入图片描述
在这里插入图片描述
题解:
先考虑一个显然错误的贪心。遍历一遍,当 ai<hi 时,就令 ai+=2,ai+1++。否则 i++。这样只能保证前面的 i 的操作次数最少,不能保证总操作次数最少。
考虑三种反悔操作:
若之前执行了 ai+=2,ai+1++。考虑撤销这个操作,改成 ai++,ai+1+=2,然后再加一步 ai++,ai+1+=2。这样使得 ai 不变,并且花 1 的代价让 ai+1+=3。
若之前执行了 ai+=3。考虑撤销这个操作,改成 ai++,ai+1+=2,然后再加一步 ai+=2,ai+1++。这样使得 ai 不变,并且花 1 的代价让 ai+1+=3。
若之前执行了 ai+=3。考虑撤销这个操作,但是加上 3 次的 ai++,ai+1+=2。这样使得 ai 不变,并且花 2 的代价让 ai+1+=6。
我们的贪心还是保证靠前的 ai 的操作次数尽量小。因此记一下之前用了几个 ai-1+=3,用了几个 ai-1+=2,ai++,即可知道能用多少次 ai+=3。如果次数都用完了还是 ai<hi,设此时 hi-ai=k,那么我们用
k2 次 ai+=2,ai+1++,用 k%2 次 ai++,ai+1+=2 即可。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+7;
int t1,t2,t3,h[N],a[N],n;
ll ans;

int main()
{
	scanf("%d",&n);
	for(int i=1; i<=n; i++) scanf("%d",&h[i]);
	for(int i=1; i<=n; i++)
	{
		h[i]=max(0,h[i]);
		t1=min(h[i]/3,a[i]);
		h[i]-=t1*3;
		t2=h[i]/2;
		h[i]-=t2*2;
		t3=h[i];
		h[i]-=t3;
		ans+=t1+t2+t3;
		a[i+1]+=t1*2+t2;
		h[i+1]-=t2+t3*2; 
	}
	printf("%lld",ans);
}

问题 E: 小 Biu 的旅行 (travel)
题目描述
小Biu所在的城市有n个景点,有一些景点之间有单向联通的道路,现在小Biu在1号景点上,他想知道到达除了1号景点之外的每个景点分别最少需要经过多少条道路?
在这里插入图片描述
如图所示为样例数据,可以知道小Biu到达2号景点的最短路线为1->2,到达3号景点的最短路线为1->3,到达4号景点的最短路线为1->2->4,到达5号景点的最短路线为1->3->5,到达6号景点的最短路线为1->3->5->6。所以答案分别为1,1,2,2,3。

输入
第1行两个正整数n,m,n表示景点的个数,n表示路径的条数。
第2行至第m-1行,每行两个u,v,表示u到v有一条单向联通的道路,数据保证没有重边和自环。

输出
输出n-1行,第i行表示从1号景点到达i+1号景点最少要经过几条道路,如果不能到达则输出-1。

样例输入
6 6
1 2
1 3
2 4
3 2
3 5
5 6

样例输出
1
1
2
2
3

提示
对于100%的数据,1≤u,v≤n≤1000,1≤m≤3000。

#include<bits/stdc++.h>
using namespace std;
int G[1010][1010];
int main()
{
    ios::sync_with_stdio(false);
    int n,m,s,e,w,ans=0,tip;
    cin>>n>>m;
    memset(G,0x3f3f3f3f,sizeof(G));
  
    for(int i=1;i<=n;i++) G[i][i]=0;
  
    for(int i=1;i<=m;i++){
        cin>>s>>e;
        G[s][e]=1;
    } 
  
    for(int k=1;k<=n;k++)
        for(int j=1;j<=n;j++)
             G[1][j]=min(G[1][j],G[1][k]+G[k][j]);
     
    for(int i=2;i<=n;i++)
       if(G[1][i]>=0x3f3f3f3f) cout<<-1<<endl;
       else cout<<G[1][i]<<endl;
   
    return 0;
}

问题 G: sunflower
题目描述
小 N 经常去小 T 家的花园里散步,小 T 家的花园有 N 个长的一样的亭子和 M 条道路连接着亭子,但是小 T 的花园太过于乱了,小 N 作为一个路痴经常进去了之后找不到出来的路,一直在环里面绕圈。于是小 N 要让小 T 把其中的某些路种上向日葵,使得剩下的路不会出现环。因为向日葵不方便种,而第i条路长Li,需要Li个向日葵去种,于是小 T 想知道他最少要种多少向日葵才能满足小 N 的要求呢?

输入
第一行两个整数N,M,表示花园的亭子数目和道路数目。
接下来M行,每行3个整数A,B,C,表示有一条连接着A和B的长度为C的道路。

输出
一行一个整数,表示小 T 最少需要种的向日葵数目。

样例输入
5 5
1 2 5
1 4 4
3 4 3
2 3 2
3 5 1

样例输出
2

提示
对于100%的数据
1≤ N ≤10^5,
1≤ M ≤2×10^5,
0< C ≤10^6

题解:求最大生成树,再求一下所有的边权的和,最后再相减。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+5;
const int inf = 0x3f3f3f3f;

struct node{
    int u,v,w;
} e[N];

bool cmp(node a,node b){
    return a.w>b.w;
}

int f[N],n,m;

int getf(int x){
    return f[x]==x ? x : f[x]=getf(f[x]);
}

int main()
{
    scanf("%d%d",&n,&m);
    ll sum=0;
    for(int i=1; i<=m; i++) {
         scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
         sum+=e[i].w;
    }
    sort(e+1,e+1+m,cmp);
    ll ans=0;
    int cnt=0;
    for(int i=1; i<=n; i++)   f[i]=i;
    for(int i=1; i<=m; i++)
    {
        int x=getf(e[i].u);
        int y=getf(e[i].v);
        if(x==y)    continue;
        f[x]=y;
        ans+=e[i].w;
        cnt++;
        if(cnt==n-1)   break;//已经完全联通直接 break,小小的剪枝
    }
    printf("%lld\n",sum-ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米莱虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值