2020 Multi-University Training Contest 2(1005,1006,1010,1012题解)

1006 : T h e O c u l u s \color{Red}1006:The Oculus

: + 算法:取模+暴力

a , b , c , 虽然a,b,c非常的大,但是我们可以直接暴力取模

, 模数你任意定,暴力预处理取模后的斐波那契数列

a , b , c , 再暴力计算a,b,c,一项一项加上对应的斐波那契并取模

c 0 , 1 , a b = = c 然后枚举c的每一位为0的位置,试着改成1,看看能否实现a*b==c

: \color{Red}原理:上面的操作只涉及加法和乘法

所以取模不会改变彼此的关系

, 但是这里用了双模数判断,为了保险一点

/*
  Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define endl '\n'

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;

inline ll read() {
    ll f = 1, x = 0;
    char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return f * x;
}
const int N = 2e6 + 10;
const int mod1 = 1e9 + 7, mod2 = 1e9 + 9;
int a[N], b[N];
int x[N];
int main() {
    a[1] = b[1] = 1, a[2] = b[2] = 2;
    for(int i = 3; i < N; i++) {
        a[i] = (a[i - 1] + a[i - 2]) % mod1;
        b[i] = (b[i - 1] + b[i - 2]) % mod2;
    }
    int t = read();
    while(t--) {
        ll a1 = 0, a2 = 0, b1 = 0, b2 = 0, c1 = 0, c2 = 0;
        int n = read();
        for(int i = 1; i <= n; i++) {
            int x = read();
            if(x) {
                a1 = (a1 + a[i]) % mod1;
                a2 = (a2 + b[i]) % mod2;
            }
        }
        int m = read();
        for(int i = 1; i <= m; i++) {
            int x = read();
            if(x) {
                b1 = (b1 + a[i]) % mod1;
                b2 = (b2 + b[i]) % mod2;
            }
        }
        int k = read();
        for(int i = 1; i <= k; i++) {
            x[i] = read();
            if(x[i]) {
                c1 = (c1 + a[i]) % mod1;
                c2 = (c2 + b[i]) % mod2;
            }
        }
        ll mult1 = (a1 * b1) % mod1, mult2 = (a2 * b2) % mod2;
        int ans;
        for(int i = 1; i <= k; i++) {
            if(x[i]) continue;
            if((c1 + a[i]) % mod1 == mult1 && (c2 + b[i]) % mod2 == mult2) {
                ans = i;
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}


1010 : L e a d o f W i s d o m \color{Red}1010:Lead of Wisdom

: 算法:搜索

比较容易想到的是折半搜索

, ( ) 按照每类物品的数量进行排序,然后均分到两个集合中(保证物品均匀分配)

d f s a , b , c , d 在两个集合中dfs出所有可能的a,b,c,d组合

a , b , c , d , 暴力枚举两个集合搜出的a,b,c,d组合,计算答案

#include <bits/stdc++.h>
using namespace std;
#define int long long
struct p{
	int ok,a,b,c,d;
}w[100009];
vector<p>vec[59],svec[59],q,r;
int n,k,shu[52],id[52],ans,top1,top2;
bool com(int a,int b){
	return shu[a]>shu[b];
}
void dfs(int pos,int a,int b,int c,int d)
{
	if( pos==k+1 )
	{
		q.push_back((p){0,a,b,c,d});
		return;
	}
	for(int i=0;i<vec[pos].size();i++)
	{
		p e=vec[pos][i];
		dfs(pos+1,a+e.a,b+e.b,c+e.c,d+e.d);
	}
	if( vec[pos].size()==0 )
		dfs(pos+1,a,b,c,d);
}
void dfs1(int pos,int a,int b,int c,int d)
{
	if( pos==k+1 )
	{
		r.push_back((p){0,a,b,c,d});
		return;
	}
	for(int i=0;i<svec[pos].size();i++)
	{
		p e=svec[pos][i];
		dfs1(pos+1,a+e.a,b+e.b,c+e.c,d+e.d);
	}
	if( svec[pos].size()==0 )
		dfs1(pos+1,a,b,c,d);
}
signed main()
{
	int t;
	cin >> t;
	while( t-- )
	{
		q.clear(); r.clear();
		cin >> n >> k;
		ans=0;
        for(int i=1;i<=50;i++)	id[i]=i;
		for(int i=0;i<=50;i++)	vec[i].clear(),svec[i].clear();
		for(int i=1;i<=n;i++)
		{
			cin >> w[i].ok >> w[i].a >> w[i].b >> w[i].c >> w[i].d;
			shu[ok]++;//记录每类物品的数量 
		}
		sort(id+1,id+1+k,com);//按照数量排序 
		for(int i=1;i<=k;i++)
		{
			int sd=id[i];
			if( i%2==1 )//给vec 
			{
				for(int j=1;j<=n;j++)
					if( w[j].ok==sd )	vec[ w[j].ok ].push_back(w[j]);	
			}
			else//给svec 
			{
				for(int j=1;j<=n;j++)
					if( w[j].ok==sd )	svec[ w[j].ok ].push_back(w[j]);	
			}
		}
		dfs(1,0,0,0,0);
		dfs1(1,0,0,0,0);
		for(int i=0;i<q.size();i++)
		{
			for(int j=0;j<r.size();j++)
			{
				p qq=q[i],ww=r[j];
				int temp=(100+qq.a+ww.a)*(100+qq.b+ww.b)*(100+qq.c+ww.c)*(100+qq.d+ww.d);
				ans=max(ans,temp);
			}
		}
		cout << ans << endl;
	}
}


1012 : S t r i n g D i s t a n c e \color{Red}1012:String Distance

: + d p 算法:序列自动机+dp

n x t [ i ] [ j ] 由序列自动机预处理nxt[i][j]

a i j 表示a串在i位置后最快出现的j字母在哪一个位置

d p [ i ] [ j ] a i b , j 令dp[i][j]为a串从i位置开始和b匹配,匹配了j个字母的最小结束位置

那么转移方程的伪代码是

for(int i=1;i<=m;i++)//枚举匹配了b串的i个字母
for(int j=i;j<=m;j++)//枚举匹配i个字母时的结尾字母
for(int q=i-1;q<=j;q++)//枚举匹配i-1个字母时的结尾字母
{
	int last=dp[i-1][q];//上一个字母在last位置完成匹配
	int kk=nxt[last][ b[j]-'a' ];//那么在last后b[j]最快出现的位置在这里
	dp[i][j]=min(dp[i][j],kk);
}

A C AC代码

#include <bits/stdc++.h>
using namespace std;
int dp[22][22],nxt[100009][27],ans[100009][22];
char a[100009],b[100009];
void make_nxt(int nxt[][27],int len,char a[])//构造nxt数组 
{
	for(int i=0;i<=25;i++)	nxt[len][i]=1e9;
	for(int i=len-1;i>=0;i--)
	{
		for(int j=0;j<=25;j++)	nxt[i][j]=nxt[i+1][j];
		nxt[i][a[i+1]-'a']=i+1;//一次只会更新a[i+1]-'a'的值 
	}
}
int main()
{
	int t;
	cin >> t;
	while( t-- )
	{
		cin >> (a+1) >> (b+1);
		int m=strlen(b+1),n=strlen(a+1);
		make_nxt(nxt,n,a);
		for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			ans[i][j]=1e9;//以i开头匹配j个字母的最小结束位置 
		for(int i=1;i<=n;i++)
		{
			//匹配j个字母,以q结尾 
			memset(dp,0x7f,sizeof(dp));
			int df=dp[0][0];
			for(int q=1;q<=m;q++)//预处理匹配一个字母的情况 
			{
				int last=nxt[i-1][ b[q]-'a' ];
				if( last==1e9 )	continue;
				dp[1][q]=last;
				ans[i][1]=min(ans[i][1],last);
			}
			for(int j=2;j<=m;j++)
			for(int q=j;q<=m;q++)//匹配j个以q结尾 
			{
				for(int w=j-1;w<q;w++)//从w转移过来 
				{
					int last=dp[j-1][w];
					int zq=b[q]-'a';
					if( last==df )	continue;
					if( nxt[ last ][ zq ]==1e9 )	continue;
					dp[j][q]=min(dp[j][q],nxt[ last ][ zq ]);
					ans[i][j]=min( ans[i][j],dp[j][q] );
				}
			}
		}
		int c;
		cin >> c;
		for(int i=1,l,r;i<=c;i++)
		{
			scanf("%d%d",&l,&r);
			int pipei=0;
			for(int j=m;j>=1;j--)
			{
				if( ans[l][j]<=r )
				{
					pipei=j;
					break;
				}
			}
			printf("%d\n",m+(r-l+1)-2*pipei);
		}
	}
}

1005 : N e w E q u i p m e n t s \color{Red}1005:New Equipments

: 算法:最小费用最大流

: A C , 注意:一下只是口头AC,所以只介绍思想没有代码

, a i j 2 + b i j + c i 观察到对于每一个人来说,花费是形如a_ij^2+b_ij+c_i的二次函数

a i > 0 , 由于a_i>0,所以二次函数开口向上

j b 2 a 当机器号j取\frac{-b}{2a}时函数值最小

n 所以在这个点附近的n个点都可能作为机器被选

? 为什么不直接选这个对称轴?因为让给别人选可能更优

所以类似二分图匹配

n 1 , 1 源点向n个人连边权为1的点,选出来的所有机器点向汇点连边权1的边

( ) , 每个人向每台被选出来的机器连一条边(因为每个人都可以用任意的机器),边权无穷

跑最小费用最大流

代码没有,因为没时间啦!!

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/107541785