2020杭电多校第二场题解1001,1006,1010,1012

发布于 2020-07-24  0 次阅读


1.Total Eclipse(并查集)

思路:

很明显的对于一个连通块需要执行其中点权值最小的那个,连通块也可能分裂, 只要每次找个连通块反复删最小的权值即可,但这种情况不好实现,所以换下面这种方法
从最大权值的点倒着添加边,也就是新增点时,小权值点向一个大权值点连接边时,小取值需要的变成0的次数可以由大权值的点承担(因为连通,在减大权值的时候小权值一起减)。
1、对输入的点按照权值非递减排序,并且初始化存边的容器,初始化集合,初始化vis。
2、出现小权值可以到达大权值的情况时,将两者归为一类,ans −= 权值

AC Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
#define INF 0x3f3f3f3f
const int N=1e5 + 9;
pair<int,int>p[N];	//存存点和并查集的值
vector<int>g[N];	//存边
int T, n, m, u, v;
long long ans;
int f[N], vis[N];

int fd(int u){
	if(f[u]==u)	return u;
	return f[u]=fd(f[u]);
}

void link(int u, int v){
	int uu=fd(u), vv=fd(v);
	if(uu!=vv)
		f[uu]=vv;
	return ;
}

void solve(){
	scanf("%d %d", &n, &m);
	for(int i=1; i<=n; i++){
		scanf("%d", &p[i].first), p[i].second=i;
		g[i].clear(), f[i]=i, vis[i]=0;
	}
	for(int i=1; i<=m; i++){
		scanf("%d %d", &u, &v);
		g[u].push_back(v), g[v].push_back(u);
	}
	sort(p+1, p+1+n);
	ans=0;
	for(int i=n; i>=1; i--){
		ans+=p[i].first;
		u=p[i].second;
		for(int it: g[u])			//遍历可到达的点
			if(vis[it])				//可到达点在该点之前(权值比该点大)
				if(fd(it)!=fd(u))	//两者不在同一集合
					link(u, it), ans-=p[i].first;	//连通两点,该点删除的次数由大权值点承担
		vis[u]=1;
	}
	printf("%lld\n", ans);
	return ;
}

int main(){
	scanf("%d", &T);
	while(T--)	solve();
	return 0;
}

6.The Oculus

思路:

原本A*B=C,但C的一位1被改成了0,所以A + B = C + f [ k ] ,变换一下f [ k ] = A + B − C ,所以可以直接暴力求A,B,C的数,因为斐波那契数在这里非常大,所以需要使用哈希,但是这里可以神奇的直接用2^64当作模数,也就是unsigned long long,所有不需要取模,它的取模自然溢出就当取模了(从0开始的)。

AC Code:

#include<cstdio>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
const int N=2e6 + 9;
ull a, b, c;
int idx;
ull f[N];

void init(){            //斐波那契打表
    f[1]=1ull, f[2]=2ull;
    for(ull i=3; i<=N; i++) f[i]=f[i-1]+f[i-2];
    return ;
}

ull write(){        //暴力求A,B,C
    int x, step;
    ull ans=0;
    cin>>x;
    for(ull i=1; i<=x; i++){
        cin>>step;
        if(step==1) ans+=f[i];
    }  
    return ans;
}

void solve(){
    a=write();
    b=write();
    c=write();
    ull temp=a*b-c;     //求f[k]
    for(ull j=1; ; j++){
        if(f[j]==temp) {idx=j; break;}      //求变化的那个斐波那契的位置
    }
    cout<<idx<<endl;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
    init();
    int T;
    cin>>T;
    while(T--)  solve();
    return 0;
}

10.Lead of Wisdom

思路:

直接暴力DFS,每种类型往下面深搜就完事了

AC Code:

#include<cstdio>
#include<iostream>
#include<cmath>
#include<vector>
using namespace std;
// #define TDS_ACM_LOCAL
const int N=59;
typedef long long ll;
struct node
{
    ll a, b, c, d;
};
vector<node>v[N];
int n, m, k, sum;
int vis[N];
ll a, b, c, d;
void init(){
    sum=0;
    for(int i=1; i<=m; i++) vis[i]=0, v[i].clear();
    return ;
}

ll dfs(int k, ll a, ll b, ll c, ll d){
    if(k==sum+1)        //到底了就return答案
        return (100ll + a)*(100ll + b)*(100ll + c)*(100ll +d);
    ll ans=0;
    for(auto it: v[k])      //遍历该种类型,然后深搜
        ans=max(ans, dfs(k+1, a+it.a, b+it.b, c+it.c, d+it.d));
    return ans;
}

void solve(){
    cin>>n>>m;
    init();                     //初始化一下容器之类的
    for(int i=0; i<n; i++){
        cin>>k>>a>>b>>c>>d;
        if(!vis[k]) vis[k]= ++sum;          //记录种类和种类个数
        v[vis[k]].push_back({a,b,c,d});     //按照记录的种类存
    }
    ll ans=dfs(1, 0, 0, 0, 0);
    cout<<ans<<endl;
    return ;
}   

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
#ifdef TDS_ACM_LOCAL
    freopen("D:\\VS code\\.vscode\\testall\\in.txt", "r", stdin);
    freopen("D:\\VS code\\.vscode\\testall\\out.txt", "w", stdout);
#endif
    int T;
    cin>>T;
    while(T--)  solve();
    return 0;
}

12.String Distance(dp)

思路:

1、预处理a aa串中每个字符第一次出现的位置
2、每次查询区间时,dp表示查询区间第i位是b串的j位时,对应的a的位置,且<=right

AC Code:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
const int N=1e5 + 9;
char a[N], b[30];
int lena, lenb;
int loc[N][30], dp[30][30];
int T, TT, l, r, mx, ans;
void init(){
	scanf("%s %s", a+1, b+1);
	lena=strlen(a+1), lenb=strlen(b+1);
	for(int i=0; i<26; i++)		loc[lena][i]=INF;	//初始化出现的位置
	//记录a中每个字符第一次出现的位置(从后往前记录,方便每次遇到相同的覆盖上一次
	for(int i=lena-1; i>=0; i--){								
		for(int j=0; j<26; j++)	loc[i][j]=loc[i+1][j];	//将后面存好的行放进当前行,因为数组a从后往前匹配的
		loc[i][a[i+1]-'a']=i+1;								//将该当前的字符出现的位置记录
	}
}

void solve(){
	init();
	scanf("%d" , &TT);
	while(TT--){
		scanf("%d %d", &l, &r);
		ans=0;
		memset(dp, INF, sizeof(dp));							//dp表示查询区间第i位是b串的j位时,对应的a的位置
		for(int j=1; j<=lenb; j++)	dp[1][j]=loc[l-1][b[j]-'a'];	//将查询区间的最左边存入dp

		for(int i=1; i<=lenb; i++){
			for(int j=1; j<=lenb; j++){
				if(dp[i][j]<=r){					//合理位置
					ans=max(ans, i);				//最长匹配长度
					for(int k=j+1; k<=lenb; k++)
						dp[i+1][k]=min(dp[i+1][k], loc[dp[i][j]][b[k]-'a']);	//计算后面一行
				}
			}
		}
		ans=(r-l+1-ans)+(lenb-ans);
		printf("%d\n", ans);
	}
	return ;
}

int main(){
	scanf("%d", &T);
	while(T--)	solve();
	return 0;
}


平平无奇的大学在读本科生