您的位置:首页 > 大数据 > 人工智能

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

2015-07-29 11:10 405 查看
昨天打完的第三场,相对而言发挥的还算正常,毕竟弱渣一只,不过有几道简单题都没看,如果放弃那道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;
}


累了....留坑待填。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: