1010 Lead of Wisdom
剪枝:预处理每一行每个位置最大值,搜到第i行时如果后面几行全部取最大值(这种情况都不一定存在)的结果还是比ans小就没必要再搜了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,s[N][N][5],sz[N];
int suf[N][5],m[N][5];
ll ans;
void dfs(int x,int a,int b,int c,int d)
{
if(1ll*(a+suf[x][0])*(b+suf[x][1])*(c+suf[x][2])*(d+suf[x][3])<=ans) return ;
if(x==k+1)
{
ans=max(ans,1ll*a*b*c*d);
return ;
}
if(sz[x]==0) dfsLead of Wisdom(x+1,a,b,c,d);
for(int i=0;i<sz[x];i++)
{
dfs(x+1,a+s[x][i][0],b+s[x][i][1],c+s[x][i][2],d+s[x][i][3]);
}
return ;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
memset(sz,0,sizeof sz);
memset(m,0,sizeof m);
for(int i=1;i<=n;i++)
{
int op;
scanf("%d",&op);
for(int i=0;i<4;i++)
{
scanf("%d",&s[op][sz[op]][i]);
m[op][i]=max(m[op][i],s[op][sz[op]][i]);
}
sz[op]++;
}
for(int i=0;i<4;i++) suf[k+1][i]=0;
for(int i=k;i>0;i--)
for(int j=0;j<4;j++)
suf[i][j]=suf[i+1][j]+m[i][j];
ans=0;
dfs(1,100,100,100,100);
printf("%lld\n",ans);
}
}
再来两种玄学做法:
反向爆搜,正向会TLE ,反向就4305ms,不明白为啥会这么快
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,s[N][N][5],sz[N];
ll ans;
void dfs(int x,int a,int b,int c,int d)
{
if(x==0)
{
ans=max(ans,1ll*a*b*c*d);
return ;
}
if(sz[x]==0) dfs(x-1,a,b,c,d);
for(int i=0;i<sz[x];i++)
{
dfs(x-1,a+s[x][i][0],b+s[x][i][1],c+s[x][i][2],d+s[x][i][3]);
}
return ;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
memset(sz,0,sizeof sz);
for(int i=1;i<=n;i++)
{
int op;
scanf("%d",&op);
for(int i=0;i<4;i++)
scanf("%d",&s[op][sz[op]][i]);
sz[op]++;
}
ans=0;
dfs(k,100,100,100,100);
printf("%lld\n",ans);
}
}
函数random_shuffle()用来对一个元素序列进行重新排序(随机的)
随机排序之后就可以过了???还是必过…
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55;
int t,n,k,a[N][4],c[N];
ll ans=0;
struct node
{
int a,b,c,d;
};
vector<vector<node>> res;
void dfs(int x,int a,int b,int c,int d)
{
if(x>k)
{
ans=max(1ll*a*b*c*d,ans);
return ;
}
for(int i=0;i<res[x].size();i++)
{
node e=res[x][i];
dfs(x+1,a+e.a,b+e.b,c+e.c,d+e.d);
}
if(res[x].size()==0) dfs(x+1,a,b,c,d);
return ;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
res.resize(k+3);//改变容量
for(int i=1;i<=k;i++) res[i].clear();
for(int i=1;i<=n;i++)
{
int e;
scanf("%d",&e);
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
res[e].push_back({a,b,c,d});
}
random_shuffle(res.begin()+1,res.begin()+1+k);
ans=0;
dfs(1,100,100,100,100);
printf("%lld\n",ans);
}
}
1006 The Oculus
双哈希
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2000010;
const ll mod=1e9+7,mod1=1e9+9;
int t,a[N],b[N],c[N];
ll f[N],f1[N],sum[N];
void init()
{
f[1]=f1[1]=1;f1[2]=f[2]=2;
for(int i=3;i<N;i++)
{
f[i]=(f[i-1]+f[i-2])%mod;
f1[i]=(f1[i-1]+f1[i-2])%mod1;
}
return ;
}
int main()
{
init();
scanf("%d",&t);
while(t--)
{
scanf("%d",a);
for(int i=1;i<=a[0];i++) scanf("%d",a+i);
scanf("%d",b);
for(int i=1;i<=b[0];i++) scanf("%d",b+i);
scanf("%d",c);
for(int i=1;i<=c[0];i++) scanf("%d",c+i);
ll res=0,res1=0,res2=0;
for(int i=1;i<=a[0];i++)
res1=(res1+a[i]*f[i])%mod;
for(int i=1;i<=b[0];i++)
res2=(res2+b[i]*f[i])%mod;
for(int i=1;i<=c[0];i++)
res=(res+c[i]*f[i])%mod;
ll cnt=0,cnt1=0,cnt2=0;
for(int i=1;i<=a[0];i++)
cnt1=(cnt1+a[i]*f1[i])%mod1;
for(int i=1;i<=b[0];i++)
cnt2=(cnt2+b[i]*f1[i])%mod1;
for(int i=1;i<=c[0];i++)
cnt=(cnt+c[i]*f1[i])%mod1;
for(int i=c[0];i>0;i--)
{
if(c[i]==0)
{
if((res+f[i])%mod==(res1*res2)%mod)
{
printf("%d\n",i);
break;
}
}
}
}
return 0;
}
1001 Total Eclipse
首先假设每个点都是独立的,sum=所有bi的和。按照bi的值从大到小排序后,遍历每个节点i,遍历与i相连的所有节点j,如果i,j还不是同一个连通块并且bi<=bj,sum-=b[i],并将i,j所在连通块合并;否则暂时不用管。已经遍历过的点一定大于等于当前点。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,M=400010;
int t,n,m,h[N],e[M],ne[M],idx;
int pre[N],b[N];
bool st[N];
struct node
{
int id,val;
bool operator < (const node &w)const
{
return val>w.val;
}
}ed[N];
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a];h[a]=idx++;
}
int find(int x)
{
return (pre[x]==x)?x:(pre[x]=find(pre[x]));
}
bool check(int a,int b)
{
int fa=find(a);
int fb=find(b);
if(fa!=fb)
{
pre[fb]=fa;
return 1;
}
else return 0;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) h[i]=-1,pre[i]=i,st[i]=0;
ll sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",b+i);
sum+=b[i];
ed[i]={i,b[i]};
}
sort(ed+1,ed+n+1);
idx=0;
while(m--)
{
int a, b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)
{
int u=ed[i].id;
for(int j=h[u];j!=-1;j=ne[j])
{
int v=e[j];
if(st[v]&&check(u,v))
{
sum-=ed[i].val;
}
}
st[u]=1;
}
printf("%lld\n",sum);
}
return 0;
}
1012 String Distance
求区间[l,r]的a串与整个b串的lcs,先预处理a串,suf[i][j]表示a串中第i个字符后第一个字符(j+‘a’)的位置。
f[i][j]表示lcs的第i是b串的第j个字符时 这个字符在a串的第一个位置,
如果f[i][j]<=r那么满足题意继续更新下一个f[i+1][],
f[i+1][k]=min(f[i+1][k],suf[ f[i][j] ] [ b[k]-‘a’ ] ) (k>j)
否则就不符合题意了。
最终的lcs就是 满足f[i][j]<=r的最大的i。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int t,n,m;
char a[N],b[25];
int suf[N][30];
int f[30][30];
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%s",a+1);
scanf("%s",b+1);
n=strlen(a+1);
m=strlen(b+1);
for(int i=0;i<26;i++) suf[n][i]=n+1;
for(int i=n-1;i>=0;i--)
{
for(int j=0;j<26;j++)
suf[i][j]=suf[i+1][j];
suf[i][a[i+1]-'a']=i+1;
}
int q;scanf("%d",&q);
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
int lcs=0;
for(int i=1;i<=m;i++)
f[1][i]=suf[l-1][b[i]-'a'];
for(int i=2;i<=m;i++)
for(int j=1;j<=m;j++) f[i][j]=r+1;
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j++)
if(f[i][j]<=r)
{
lcs=max(lcs,i);
for(int k=j+1;k<=m;k++)
f[i+1][k]=min(f[i+1][k],suf[f[i][j]][b[k]-'a']);
}
printf("%d\n",r-l+1+m-lcs*2);
}
}
return 0;
}
New Equipments
每个人与设备的花费函数是单谷函数,最小值点可以三分出来或者直接用 -b/(2*a)
总共只有n个人,最多匹配n次,所以每个人只用与产生花费最小的n太设备建边就好,边的容量为1,单价费用就是产生的花费,然后再把n个人与源点建边,设备与汇点建边,边权都是1花费都是0.最后跑费用流就好。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=10010,M=100010;
const ll inf=0x3f3f3f3f3f3f3f3f;
int _,s,t,n,m,h[N],ne[M],e[M],w[M],idx;
ll a[N],b[N],c[N],cost[M];
ll ans[N];
void add(int a,int b,int c,ll d)
{
e[idx]=b;ne[idx]=h[a];w[idx]=c;cost[idx]=d;h[a]=idx++;
e[idx]=a;ne[idx]=h[b];w[idx]=0;cost[idx]=-d;h[b]=idx++;//反向边费用为负,能把费用退回去
}
ll get(int i,int j)
{
return a[i]*j*j+b[i]*j+c[i];
}
bool flag,st[N];
ll dist[N],dep[N];
int pre[N];
bool spfa()
{
for(int i=s;i<=t;i++) dist[i]=inf,pre[i]=-1,st[i]=0;
deque<int> q;
q.push_back(s);
dist[s]=0;
st[s]=1;
while(q.size())
{
int u=q.front();q.pop_front();
st[u]=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(dist[v]>dist[u]+cost[i]&&w[i]>0)
{
dist[v]=dist[u]+cost[i];
pre[v]=i;//记录最短路从那条边过来的,方便回溯
if(!st[v])
{
if(q.size()&&dist[v]<dist[q.front()]) q.push_front(v);//SLF优化
else q.push_back(v);
st[v]=1;
}
}
}
}
return pre[t]!=-1;
}
ll mincost;
void mcmf()
{
int k=1,flow=0;
while(spfa())
{
for(int i=pre[t];i!=-1;i=pre[e[i^1]])
{
w[i]-=1;
w[i^1]+=1;
mincost+=cost[i];
}
ans[k++]=mincost;
if(k>n) break;
}
for(int i=k;i<=n;i++) ans[i]=ans[i-1];
return ;
}
int main()
{
scanf("%d",&_);
while(_--)
{
scanf("%d%d",&n,&m);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) scanf("%lld%lld%lld",a+i,b+i,c+i);
map<int,int> mp;
int cnt=n+1;
idx=0;
for(int i=1;i<=n;i++)
{
int l=1,r=m;
while(l<r)
{
int mid1=l+(r-l)/3;
int mid2=r-(r-l)/3;
if(get(i,mid1)<get(i,mid2)) r=mid2-1;
else l=mid1+1;
}
int res=n-1,pos=l;
if(mp.count(l)==0) mp[l]=cnt++;
add(i,mp[l],1,get(i,l));
l++;
while(res--)//选择总共n台设备
{
if(mp.count(l)==0) mp[l]=cnt++;
add(i,mp[l],1,get(i,l));
if(l<=pos)
{
if(pos+pos-l+1<=m) l=pos*2-l+1;
else l--;
}
else
{
if(pos-l+pos>=1) l=pos-l+pos;
else l++;
}
}
}
t=cnt;
for(int i=1;i<=n;i++) add(s,i,1,0);
for(int j=n+1;j<cnt;j++) add(j,t,1,0);
mincost=0;
mcmf();
for(int i=1;i<=n;i++) printf("%lld%c",ans[i],i<n?' ':'\n');
}
}