G.开箱-----dp问题
内网链接
思路:
将每一个钥匙所能打开的宝箱用二进制表示成一种状态.
状态转移f [ j | state ]=min ( f[ j | state] , f[ j ] + w )
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int f[1<<13];
int main()
{
int n,m;
cin>>n>>m;
memset(f,0x3f,sizeof f);
f[0]=0;
for(int i=0;i<m;i++)
{
int w,x,cnt,state=0;
cin>>w>>cnt;
while(cnt--)
{
cin>>x;
state|=(1<<x-1);
}
for(int j=0;j<(1<<n);j++)f[j|state]=min(f[j|state],f[j]+w);
}
if(f[(1<<n)-1]==0x3f3f3f3f)cout<<-1<<endl;
else cout<<f[(1<<n)-1]<<endl;
}
I.小西的区间问题
内网链接
复杂度:O( n logn )
思想:枚举每一个区间[l , r]
,看有多少个区间与他相交,其它区间如果想和它相交的话需要满足
ri >= l
, li <= r
,第i
个区间之前的区间全部满足第二个条件,排序后用一个mulset
维护第i
个区间之前的满足第一个条件的区间的数量,第i
个区间之后区间全部满足第一个条件,然后在区间中查找第一个比r
大的li
记录下来,减去i的位置,算出i后满足两个条件的区间的数量,加起来,求最大值即为答案
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define x first
#define y second
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 200010;
PII a[N];int n;
int main()
{
cin>>n;
for(int i=0;i<n;i++)cin>>a[i].x>>a[i].y;
sort(a,a+n);
multiset<int>r;
int res=0;
for(int i=0;i<n;i++)
{
if(r.size()&&*r.begin()<a[i].x)r.erase(r.begin());
int pos=r.size()+upper_bound(a,a+n,(PII){a[i].y,INF})-a-i;
res=max(res,pos);
r.insert(a[i].y);
}
cout<<res<<endl;
}
J.NANA的冒险-----tarjan缩点+记忆化dfs
内网链接
算法 tarjan缩点+dfs记忆化搜索
思路:首先将图建边缩点,在把缩过点的图重新建一个无环图,然后dfs利用回溯记录一下时间就行了,空间开的巨多,哈哈哈
大早上来补一道图论题,一点点错误找了半天。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 200010,M=400010;
int dist[N],res[N],w[N];
int h[N],hs[N],e[M],ne[M],idx=0;
int scc_cnt=0,top=0,timestamps=0;
int stk[N],dfn[N],low[N];
bool in_stk[N],st[N];
int din[N];
int id[N];
int n,m;
void add(int h[],int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u)
{
dfn[u]=low[u]=++timestamps;
stk[++top]=u;
in_stk[u]=true;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(in_stk[j])low[u]=min(low[u],dfn[j]);
}
if(dfn[u]==low[u])
{
++scc_cnt;
int y;
do{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
dist[scc_cnt]+=w[y];
}while(u!=y);
}
}
int dfs(int u)
{
if(st[u])return dist[u];
for(int i=hs[u];i!=-1;i=ne[i])
{
int j=e[i];
dist[u]+=dfs(j);
}
st[u]=true;
return dist[u];
}
int main()
{
memset(h,-1,sizeof h);
memset(hs,-1,sizeof hs);
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
add(h,i,x);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])tarjan(i);
}
for(int i=1;i<=n;i++)
{
for(int j=h[i];j!=-1;j=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];
if(a!=b)
{
add(hs,a,b);
din[b]++;
}
}
}
for(int i=1;i<=scc_cnt;i++)
if(!din[i])dfs(i);
for(int i=1;i<=n;i++)
{
int t=id[i];
cout<<dist[t]<<endl;
}
}
K.77777777plus 爱数学-----数学思维
内网链接
思路:纯数学问题,要找末尾0
,就找出现的10
的因数2
和5
,当x
为奇数时每次x-2
,不可能出现因子2
所以奇数的末尾0
必然为0
再来看偶数,
先把偶数x/2
,这样变成连续的x
,即求1*2*3....*x
有几个末尾0
,即每次答案吧加上x/5
直到x变为0
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int main()
{
LL x;
cin>>x;
if(x%2)
{
cout<<0<<endl;
}
else
{
x/=2;
LL res=0;
while(x)
{
res+=x/5;
x/=5;
}
cout<<res<<endl;
}
}