2015 Multi-University Training 3 多校集训 第三场 部分题解及反思

昨天打完的第三场,相对而言发挥的还算正常,毕竟弱渣一只,不过有几道简单题都没看,如果放弃那道1010的DP的话还是可以多做两题的,DP依旧是硬伤。

最后做了5道题,勉强挤进第二页。如果能把速度提上去的话应该可以进第一页的。。DP题卡了2个小时不多说。不过赛后看官方题解说DFS就可以了,感觉有点崩溃。



部分题解:

HDU 5316 A.Magician

题意:

给你一个长度不超过10W的数组,10W个询问。询问分为两种操作。

0 X Y 查询X到Y的区间里面的一个和最大的子序列,要求子序列中相邻的数的位置的奇偶性不同。

1 X Y 将数字a[x]修改为Y


题解:

很明显的一道线段树。当然是线段树谁都能看出来。。。

线段树维护四个值,奇数开始偶数结尾的子序列的最大值,奇数开始奇数结尾的子序列的最大值,还有偶数开始奇数结尾和偶数开始偶数结尾。 如果不存在则值记为负Inf(题目要求子序列不能为空)

合并的操作:

奇数开始偶数结尾记为 JO 奇数开始奇数结尾记为JJ 偶数开始偶数结尾记为OO 偶数开始奇数结尾记为OJ 

OJ+OJ=OJ

OJ+OO=OO

JO+JO=JO

JO+JJ=JJ

JJ+OO=JO

JJ+OJ=JJ

OO+JO=OO

OO+JJ=OJ

最后选取最大值即可。

#include <bits/stdc++.h>
using namespace std;
int x;
int y;
const long long dinf=-1e18;
const int maxn=100005;
long long jon;
long long ojn;
long long oon;
long long jjn;
int a[maxn];
long long jo[maxn*4];
long long oj[maxn*4];
long long oo[maxn*4];
long long jj[maxn*4];
long long max(long long a,long long b)
{
    if (a>b) return a;
    return b;
}
void build(int l,int r,int o)
{
    if (l==r)
    {
        if (l%2)jj[o]=a[l];
        else oo[o]=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,2*o);
    build(mid+1,r,2*o+1);
    jo[o]=max(max(jj[2*o]+oo[2*o+1],jo[2*o]+jo[2*o+1]),max(jo[2*o],jo[2*o+1]));
    oj[o]=max(max(oo[2*o]+jj[2*o+1],oj[2*o]+oj[2*o+1]),max(oj[2*o],oj[2*o+1]));
    oo[o]=max(max(oj[2*o]+oo[2*o+1],oo[2*o]+jo[2*o+1]),max(oo[2*o],oo[2*o+1]));
    jj[o]=max(max(jo[2*o]+jj[2*o+1],jj[2*o]+oj[2*o+1]),max(jj[2*o],jj[2*o+1]));
    return ;
}
void add(int l,int r,int o)
{
    if (r<x) return ;
    if (l>x) return ;
    if (l==r)
    {
        if (l%2) jj[o]=y;
        else oo[o]=y;
        return ;
    }
    int mid=(l+r)/2;
    add(l,mid,2*o);
    add(mid+1,r,2*o+1);
    jo[o]=max(max(jj[2*o]+oo[2*o+1],jo[2*o]+jo[2*o+1]),max(jo[2*o],jo[2*o+1]));
    oj[o]=max(max(oo[2*o]+jj[2*o+1],oj[2*o]+oj[2*o+1]),max(oj[2*o],oj[2*o+1]));
    oo[o]=max(max(oj[2*o]+oo[2*o+1],oo[2*o]+jo[2*o+1]),max(oo[2*o],oo[2*o+1]));
    jj[o]=max(max(jo[2*o]+jj[2*o+1],jj[2*o]+oj[2*o+1]),max(jj[2*o],jj[2*o+1]));
    return ;
}
void query(int l,int r,int o)
{
    if (r<x) return ;
    if (l>y) return ;
    if (x<=l&&r<=y)
    {
        long long a=jon;
        long long b=ojn;
        long long c=oon;
        long long d=jjn;
        jon=max(max(d+oo[o],a+jo[o]),max(a,jo[o]));
        ojn=max(max(c+jj[o],b+oj[o]),max(b,oj[o]));
        oon=max(max(b+oo[o],c+jo[o]),max(c,oo[o]));
        jjn=max(max(a+jj[o],d+oj[o]),max(d,jj[o]));
        return ;
    }
    int mid=(l+r)/2;
    query(l,mid,2*o);
    query(mid+1,r,2*o+1);
    return ;
}
void print(int l,int r,int o)
{
    printf("%d %d %d %lld %lld %lld %lld\n",l,r,o,jo[o],oj[o],oo[o],jj[o]);
    if (l==r) return ;
    int mid=(l+r)/2;
    print(l,mid,2*o);
    print(mid+1,r,2*o+1);
}
int main()
{
    int t,n,q,m;
    int type;
    scanf("%d",&t);
    while (t--)
    {
        
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        memset(jo,0,sizeof(jo));
        memset(oj,0,sizeof(oj));
        memset(oo,0,sizeof(oo));
        memset(jj,0,sizeof(jj));
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        for (int i=1;i<maxn*4;i++) 
        {
            jo[i]=dinf;
            oj[i]=dinf;
            oo[i]=dinf;
            jj[i]=dinf;
        }
        build(1,n,1);
        while (m--)
        {
            scanf("%d%d%d",&type,&x,&y);
            if (!type)
            {
                jon=ojn=oon=jjn=dinf;
                query(1,n,1);
                cout<<max(max(jon,ojn),max(oon,jjn))<<endl;
            }
            else add(1,n,1);
        }
    }
    return 0;
}




HDU 5317 B . RGCDQ


题意:

设F[i]=i的不同的素因子个数,求区间[L,R]里面的最大的gcd(f[i],f[j]),数据最大到100W


题解:

首先nlogn的算法解出每个数的素因子个数(素数筛法)。可以发现,在100W的范围内,每个数的素因子个数不超过7(2*3*5*7*11*13*17*19>100W)

那么直接枚举最终结果,然后二分查找即可。

比如查找ans=7,那么就找两个不同F[i]=7的在[L,R]的数字

赛后看别人的方法,好像只用枚举一个数字就可以了..我的方法次枚举两个数...额还是太弱。

#include <bits/stdc++.h>
using namespace std;
vector <int> v[10];
int f[1000005];

int x,y;
int find(int t)
{
	int ans=-1;
	int l=0;
	int r=v[t].size()-1;
	while (l<=r)
	{
		int mid=(l+r)/2;
		if (v[t][mid]>=x)
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return ans;
}
int gcd(int a,int b)
{
	if (!b) return a;
	return gcd(b,a%b);
}
int main()
{
	memset(f,0,sizeof(f));
	for (int i=2;i<=1000000;i++)
	{
		if (f[i]==0)
		{
			for (int j=i;j<=1000000;j+=i)
			{
				f[j]++;
			}
		}
	}
	for (int i=1;i<=7;i++) v[i].clear();
	for (int i=2;i<=1000000;i++) v[f[i]].push_back(i);
	int t,ans;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&x,&y);
		ans=1;
		for (int i=2;i<=7;i++)
		{
			int t1=find(i);
			if (t1==-1) continue;
			if (v[i][t1]>y) continue;
			for (int j=2;j<=i;j++)
			{
				if (ans>=gcd(i,j)) continue;
				int t2=find(j);
				if (t2==-1) continue;
				if (i==j) t2++;
				if (t2>=v[j].size()||v[j][t2]>y) continue;
				ans=gcd(i,j);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

HDU 5318 C.The Goddess Of The Moon


题意:

给你n个字符串,长度不超过9,如果字符串A有一个长度超过1的前缀和字符串B的后缀相同的话,则A可以接在B后面,问有多少方法可以接出一个长度为m的字符串,n最多50,m到1e9.


题解:

题比赛的时候没时间看,赛后Fuei给我讲了一下题意....心里千万草尼马踏过....竟然又漏掉一道简单题TAT。

由于n和字符串长度都很小,所以可以随便暴力一下弄出两个字符串的关系,弄成一张邻接矩阵maze。然后就是快速幂求出maze的(m-1)次方,最后把maze里面所有的数字加起来就好了。

(然而后来做的时候在前面怕超时加了一个剪枝...错误的剪枝...CHA了快一个小时....最后删掉就好了..)

#include <bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;

long long maze[55][55];
long long a[55][55];
long long b[55][55];
vector<string>s;
int n;
bool check(int x,int y,int l)
{
	int len=s[x].length();
	for (int i=0;i<l;i++)
	{
		if (s[x][len-l+i]!=s[y][i]) return false;
	}
	return true;
}
bool comp(int x,int y)
{
	int len=min(s[x].length(),s[y].length());
	for (int i=2;i<=len;i++)
	{
		if (check(x,y,i)) return true;
	}
	return false;
}
void Init()
{
	memset(a,0,sizeof(a));
	for (int i=1;i<=n;i++) a[i][i]=1;
}
void chen1()
{
	memset(b,0,sizeof(b));
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			for (int k=1;k<=n;k++)
			{
				b[i][j]+=a[i][k]*maze[k][j];
				b[i][j]%=mod;
			}
		}
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			a[i][j]=b[i][j];
		}
	}
	return ;
}
void chen2()
{
	memset(b,0,sizeof(b));
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			for (int k=1;k<=n;k++)
			{
				b[i][j]+=maze[i][k]*maze[k][j];
				b[i][j]%=mod;
			}
		}
	}
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=n;j++)
		{
			maze[i][j]=b[i][j];
		}
	}
	return ;
}
void qpow(int x)
{
	Init();
	x--;
	while (x)
	{
		if (x%2) chen1();
		chen2();
		x>>=1;
	}
	return ;
}
int main()
{
	int t,m;
	long long ans;
	string str;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		s.clear();
		for (int i=1;i<=n;i++) 
		{
			cin>>str;
			bool f=false;
			for (int j=0;j<s.size();j++)
			{
				if (s[j]==str) 
				{
					f=true;
					break;
				}
			}
			if (!f) s.push_back(str);
		}
		n=s.size();
		memset(maze,0,sizeof(maze));
		for (int i=0;i<n;i++)
		{
			for (int j=0;j<n;j++)
			{
				if (comp(i,j)) maze[i+1][j+1]=1;
			}
		}
//		for (int i=1;i<=n;i++)
//		{
//			for (int j=1;j<=n;j++)
//			{
//				printf("% 4d ",maze[i][j]);
//			}
//			printf("\n");
//		}
		qpow(m);
		ans=0;
		for (int i=1;i<=n;i++)
		{
			for (int j=1;j<=n;j++)
			{
				ans+=a[i][j]%mod;
				ans%=mod;
			}
		}
		cout<<ans<<endl;

	}
	return 0;
}


HDU 5319 D.Painter


题意:

一面墙,在上色,从左上到右下的都是R,从右上到左下的都是B,如果既有R又有B,就会显示G。给你一面墙,问至少要涂多少笔。


题解:

水题,n只有50,想怎么暴力怎么暴力

需要注意的是,给的墙不是n*n的,而是n*m, m要从输入中得出来。具体看代码。



#include <bits/stdc++.h>
using namespace std;
char s[105][105];
int n,m;
void DW1(int x,int y)
{
	while (1)
	{
		if (s[x][y]=='R') s[x][y]='.';
		else if (s[x][y]=='G') s[x][y]='B';
		else return ;
		x++;
		y++;
		if (x>=n||y>=m) return ;
	}
	return ;
}
void DW2(int x,int y)
{
	while (1)
	{
		if (s[x][y]=='B') s[x][y]='.';
		else return ;
		x++;
		y--;
		if (x>=n||y<0) return ;
	}
}
int main()
{
	int t;
	int ans;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d",&n);
		for (int i=0;i<n;i++)scanf("%s",s[i]);
		m=strlen(s[0]);
		ans=0;
		for (int i=0;i<n;i++)
		{
			for (int j=0;j<m;j++)
			{
				if (s[i][j]=='R'||s[i][j]=='G')
				{
					DW1(i,j);
					ans++;
				}
			}
		}
		for (int i=0;i<n;i++)
		{
			for (int j=0;j<m;j++)
			{
				if (s[i][j]=='B')
				{
					ans++;
					DW2(i,j);
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}







累了....留坑待填。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值