[JSOI2010]解题报告+2010~2011小结
[JSOI2010~2011]小结
有一些题目还没写,大概就是考了DP,一些数据结构,还有组合数学和图论这些东西,其实主要是思维难度比较大,比如挖宝藏的问题转化和怎么简便的转移,这些都要在平时多训练,提高思维能力
[JSOI2010]旅行
[JSOI2010]找零钱的洁癖
乱搞,我写的是bfs+贪心
#include<bits/stdc++.h>
#define int long long
using namespace std;
int x,n,a[53],an[500003],num[500003];
map<int,int>mp;
int ask1()
{
int sum=x,ans=0;
for(int i=n;i>=1;i--)
if(a[i]!=0)
ans+=sum/a[i],sum-=sum/a[i]*a[i];
if(sum!=0)
return 1e18;
return ans;
}
int ask2()
{
int hd=0,tl=1;
while(hd<tl)
{
hd++;
for(int i=1;i<=n;i++)
{
tl++,an[tl]=an[hd]+1;
if(tl>=200000)
return 1e18;
if(num[hd]<x)
num[tl]=num[hd]+a[i];
else
num[tl]=num[hd]-a[i];
if(num[tl]==x)
return an[tl];
if(mp[num[tl]]==1)
tl--;
mp[num[tl]]=1;
}
}
return 1e18;
}
signed main()
{
scanf("%lld",&x);
while(scanf("%lld",&a[n+1])!=EOF)
n++;
sort(a+1,a+n+1);
mp[0]=1;
if(x==0)
cout<<0;
else
cout<<min(ask1(),ask2());
return 0;
}
[JSOI2010]挖宝藏
我们可以先把问题转化一下,发现要挖到某一个宝藏,实际上就是往上取一个倒三角,那么我们可以把每个点都投影到一个区间上去,而且每一个区间都对应唯一的一个点
然后设\(dp[i]\)表示必选第\(i\)个区间的最大收益,考虑如果选了\(i\)区间,那么被完全包含的区间是必须要选的,直接把这些区间的收益算到\(i\)里面,往前枚举\(j\)转移,如果无交集就直接转移,如果有但是不是包含关系就要暴力去掉他们的交集,如果有包含关系就直接跳过
然后注意一下细节就好了
#include<bits/stdc++.h>
using namespace std;
struct node
{
int l1,r1,p1,p2,bl;
}a[1003];
int n,x,y,now,sum,ans,dp[1003];
int ask(int l,int r)
{
return (r-l+2)*(r-l+2)/4;
}
int cmp(node nx,node ny)
{
if(nx.r1!=ny.r1)
return nx.r1<ny.r1;
return nx.l1<ny.l1;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&x,&y,&a[i].p1);
a[i].l1=x+y+1,a[i].r1=x-y-1,a[i].p2=0,a[i].bl=ask(a[i].l1,a[i].r1);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(a[j].l1>=a[i].l1&&a[j].r1<=a[i].r1)
a[i].p2+=a[j].p1;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
dp[i]=a[i].p2-a[i].bl,now=1,sum=0;
for(int j=1;j<i;j++)
{
if(a[j].r1<a[i].l1)
dp[i]=max(dp[i],dp[j]+a[i].p2-a[i].bl);
if(a[j].l1<a[i].l1&&a[j].r1>=a[i].l1)
{
while(now<=i&&a[now].r1<=a[j].r1)
{
if(a[now].l1>=a[i].l1)
sum+=a[now].p1;
now++;
}
dp[i]=max(dp[i],dp[j]+a[i].p2-a[i].bl-(sum-ask(a[i].l1,a[j].r1)));
}
}
ans=max(ans,dp[i]);
}
cout<<ans;
return 0;
}
[JSOI2010]排名
手玩了好久才搞出了策略......round3的题目好毒瘤啊,这道题还算是相对简单的
可以很显然的发现,这道题需要拓扑排序,第二问很简单,每次取能取到的,最大的就行了,用堆维护一下即可
第二问稍微复杂一丢丢,我的做法是先DFS一遍,预处理这个点往后扩展的最小的点,设点\(x\)为\(minn[x]\),然后每次取能取到的,\(minn[x]\)最小的就行了,也是用堆维护
#include<bits/stdc++.h>
using namespace std;
int n,a[200003],chu[200003],minn[200003],num[200003];
vector<int>l[200003];
struct node
{
int x;
bool operator < (const node &nx)const
{
return minn[nx.x]<minn[x];
}
}p,txt;
priority_queue<node>q;
struct node1
{
int x1;
bool operator < (const node1 &nx)const
{
return nx.x1>x1;
}
}p1,txt1;
priority_queue<node1>q1;
void dfs(int x)
{
for(int j=0;j<l[x].size();j++)
{
dfs(l[x][j]);
minn[x]=min(minn[x],minn[l[x][j]]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]!=0)
l[a[i]].push_back(i),chu[i]++;
minn[i]=i;
}
for(int i=1;i<=n;i++)
if(chu[i]==0)
{
dfs(i);
p.x=i,p1.x1=i,q.push(p),q1.push(p1);
}
for(int i=1;i<=n;i++)
{
p=q.top(),q.pop(),num[p.x]=i;
for(int j=0;j<l[p.x].size();j++)
txt.x=l[p.x][j],q.push(txt);
}
for(int i=1;i<=n;i++)
cout<<num[i]<<" ";
cout<<endl;
for(int i=1;i<=n;i++)
{
p1=q1.top(),q1.pop(),num[p1.x1]=i;
for(int j=0;j<l[p1.x1].size();j++)
txt1.x1=l[p1.x1][j],q1.push(txt1);
}
for(int i=1;i<=n;i++)
cout<<num[i]<<" ";
cout<<endl;
return 0;
}