A-グローバルトラベル
トピック:
実践:参照元:これ
ベクトルタイムアウト、チェーンフォワードスターのみを使用してA
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=1e6+10;
struct node
{
int u,w,ne;
}e[N*2];
int head[N],cnt;
void add(int u,int v,int w)
{
e[++cnt]={v,w,head[u]};
head[u]=cnt;
e[++cnt]={u,w,head[v]};
head[v]=cnt;
}
vector<pii >edge;
int n;
int mx[N][2],f[N][2];
int sum=0,flag,l,r;
void dfs1(int u,int fa,int dep)
{
if(dep>sum){
sum=dep;
if(!flag) l=u;
else r=u;
}
for(int i=head[u];i;i=e[i].ne){
int v=e[i].u;
int w=e[i].w;
if(v==fa) continue;
dfs1(v,u,dep+w);
}
}
void dfs2(int u,int fa)
{
if(u==r){
flag=1;return;
}
for(int i=head[u];i;i=e[i].ne){
int v=e[i].u;
int w=e[i].w;
if(v==fa) continue;
edge.push_back(make_pair(u,v));
dfs2(v,u);
if(flag) return ;
edge.pop_back();
}
}
void init()
{
dfs1(1,-1,0);
sum=0,flag=1;
dfs1(l,-1,0);//获取直径的两个点
flag=0;
dfs2(l,-1);//获取直径的信息
}
void dfs3(int u,int fa,int flag)
{
int mx1=0,mx2=0;
for(int i=head[u];i;i=e[i].ne){
int v=e[i].u;
int w=e[i].w;
if(v==fa) continue;
dfs3(v,u,flag);
int t=mx[v][flag]+w;
if(t>=mx1) mx2=mx1,mx1=t;
else if(t>mx2) mx2=t;
mx[u][flag]=max(mx[u][flag],mx[v][flag]+w);
f[u][flag]=max(f[u][flag],f[v][flag]);
}
//if(!flag) printf("u:%d mx1:%d mx2:%d\n",u,mx1,mx2);
f[u][flag]=max(f[u][flag],mx1+mx2);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
init();
//printf("l:%d r:%d\n",l,r);
dfs3(l,-1,0);
dfs3(r,-1,1);
// for(int i=1;i<=n;++i){
// printf("i:%d r:%d mx:%d\n",i,f[i][0],mx[i][0]);
// }
int ans=1e9;
for(auto now:edge)
{
ans=min(ans,max(f[now.first][1],f[now.second][0]));
}
cout<<ans<<endl;
}
B-Recoveryシリーズ
トピック:
一番上のものから後ろに向かって、それを削除するたびに、1つが失われ、6つが追加されるため、実際の増加は5であり、問題は必ず解決されます。
次に、最上位ビットnから削除できます。キューを使用してください。数列aiも出力されるため、履歴の状態を記録する必要があります。ポップはベクトルでは保存できません。左ポインターを使用して、分解できる数を見つけます。
#include<bits/stdc++.h>
using namespace std;
vector<pair<int,int>>ans;
int n,x;
int main()
{
scanf("%d%d",&n,&x);
int len=x;
ans.push_back(make_pair(x,n-1));
int i=0;
while(len<n-1){
while(ans[i].first==0) ++i;
len+=x-1;
ans[i].first--;
ans.push_back(make_pair(x,ans[i].second-1));
}
printf("%d ",n);
for(auto now:ans){
while(now.first--) printf("%d ",now.second);
}
}
C戻る
トピック:
どのカプセルが必要かを調べてください。
サンプル分析:
あなたは単に01バックパックの考えについて考えることができます:
したがって、タイムアウトコード:
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=(b);++i) #define mem(a,x) memset(a,x,sizeof(a)) #define pb push_back #define pi pair<int, int> #define mk make_pair using namespace std; typedef long long ll; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;} const int N=1e5+10; //bitset<N>pre[500],last[500]; int a[N],n,m; bool pre[500][N],last[500][N]; map<int,int>mp; int main() { cin>>n>>m; rep(i,1,n){scanf("%d",&a[i]);mp[a[i]]++;} sort(a+1,a+1+n);n=unique(a+1,a+1+n)-a-1; pre[0][0]=1; for(int i=1;i<=n;++i){ for(int j=0;j<=m;++j){ pre[i][j]=pre[i-1][j]; int t=mp[a[i]]; for(int k=1;k<=t;++k) if(j>=k*a[i]) pre[i][j]|=pre[i-1][j-k*a[i]]; } } last[n+1][0]=1; for(int i=n;i>=1;--i){ for(int j=0;j<=m;++j){ last[i][j]=last[i+1][j]; int t=mp[a[i]]; for(int k=1;k<=t;++k) if(j>=k*a[i])last[i][j]|=last[i+1][j-k*a[i]]; } } vector<int>ans; for(int i=1;i<=n;++i){ int flag=1; for(int j=0;j<=m&&flag;++j){ if(pre[i-1][j]&&last[i+1][m-j]){ flag=0; } } if(flag){ ans.push_back(a[i]); } } sort(ans.begin(),ans.end()); printf("%d\n",ans.size()); for(int v:ans)printf("%d ",v); }
次に、ビットセット最適化01バックパックを見てください。
対応する質問タイプ:この種類のさまざまなタイプには、数=> 1があります。タイプ間の数の合計が1e5状態でもあるため、通常の01バックパックはタイムアウトする必要があります。
ビットセットの最適化、バイナリの特性を採用、同じタイプの数は、カバレッジ状態を乗算することにより、少し高速な電力のように繰り返すことができます。
#include<cstdio> #include<iostream> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<vector> #include<ctime> #include<cmath> #include<bitset> using namespace std; typedef long long ll; const int N = 100010; bitset<N>f[451],g[451]; int a[N],cnt[N],ans[N],anss; int main() { int n,m; scanf("%d%d",&n,&m); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); sort(a + 1,a + n + 1); int tot = 0; for(int i = 1;i <= n;++i) { if(a[i] != a[i - 1]) a[++tot] = a[i]; cnt[tot]++; } n = tot; f[0][0] = 1; g[n + 1][0] = 1; for(int i = 1;i <= n;++i) { int k = a[i],tmp = cnt[i];//f[i]|=f[i]<<a[i]代表f[i]取一个a[i]时的状态转移 f[i] = f[i - 1]; for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) f[i] |= f[i] << k; // 运用倍增(二进制)的思想,节约时间 并且能够覆盖所有的状态 //比如现在有5个1 //j=1 1一个1 可以 得到 bitset状态:000011(从后往前数从低位到高位,低位从0开始) //j=2 那么倍增一下,两个1 :之前的状态00011移两位得 001100 //或上之前得000011 得 001111 是不是得到0,1,2,3,都是1的情况 //需要注意的是现在我们应该是消耗了三个1了 j目前还是2。那么tmp就不是一成不变的,所以tmp-=j //接着j继续乘2 j =4 由于5个1消耗了3 剩余两个,小于j 跳出for循环 //因为有剩余的部分,就继续组合一下 f[i] |= f[i] << (a[i] * tmp); } for(int i = n;i >= 1;--i) { int k = a[i],tmp = cnt[i]; g[i] = g[i + 1]; for(int j = 1;j <= tmp;tmp -= j,j <<= 1,k <<= 1) g[i] |= g[i] << k; g[i] |= g[i] << (a[i] * tmp); } for(int i = 1;i <= n;++i) { int flag = 0; for(int j = 0;j <= m;++j) { if(f[i - 1][j] & g[i + 1][m - j]) { flag = 1;break; } } if(!flag) ans[++anss] = a[i]; } // for(int i = 1;i <= n;++i) printf("%d %d\n",a[i],cnt[i]); printf("%d\n",anss); for(int i = 1;i <= anss;++i) printf("%d ",ans[i]); return 0; }