筱玛爱阅读
题解:状压dp
1.书的标签随便贴,所以从大到小排序
2.dp[i]表示买前 i 本可以优惠的最大价格
状态转移方程 dp[i]=max(dp[i],dp[j]+a[cnt[i]]); 表示新买了这本书是否可以凑够一个优惠方案,并且不和之前的优惠方案相冲突
dp好难…
#include<bits/stdc++.h>
using namespace std;
const int N=15;
const int M=1<<N;//所有可能存在的促销情况
int n,m,k,now,v,sum;
int a[N+5],cnt[M],dp[M];//cnt[i]记录i在二进制下有几个1
bool vis[M];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1,greater<int>());
for(int i=1;i<=m;++i)
{
scanf("%d",&k);
now=0;
while(k--)//状态压缩
{
scanf("%d",&v);
now|=(1<<(v-1));
}
vis[now]=1;
}
for(int i=1;i<M;++i)
cnt[i]=cnt[i&(i-1)]+1;//从i-lowbit(i)转移
for(int i=0;i<M;++i)
{
if(vis[i]) dp[i]=max(dp[i],a[cnt[i]]);
for(int j=i;j;j=(j-1)&i)//i的子集j 重点
{
int x=i^j;//j关于i的补集x 重点
if(!vis[x])continue;//用x来免第cnt[i]大的价格
dp[i]=max(dp[i],dp[j]+a[cnt[i]]);
}
for(int j=0;j<n;++j)
dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]);//初始化下一个状态
}
for(int i=1;i<=n;++i) sum+=a[i];
printf("%d\n",sum-dp[(1<<n)-1]);
return 0;
}
小w的a+b问题
题解:签到题,然后我不会…
注意当且仅当 -1 时不能被构造
#include<bits/stdc++.h>
using namespace std;
long long c;
int main()
{
while(scanf("%lld",&c)!=EOF)
{
~c?printf("%lld %lld\n",2147483647LL,2147483649LL+c):printf("No solution\n");
}
}
小w的a=b问题
题解:
法一:将数组中的数的阶乘中,相同数的出现的次数记录下来,然后将每一个出现过的数分解为质因数,然后比较两个数组的质因数是否完全相同。
法二:上一个方法不直接计算阶乘和乘积和的原因是,数字太大会爆long long
因此采用模计算的话就可以计算出两个数组的值,但是模计算是有误差的,难免会出现虽然实际数值不同,但取模后数值相同的情况,所以为了避免误差,使得结果尽量准确,因此采用不同的模计算,如果所有的模运算结果都相等那么则两数组相等。
注意:模计算取素数,因此所得hash值不会冲突
法二代码:(标程)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long mod[10]={10000019,10000079,10000103,10000121,10000139,10000141,10000169,10000189,10000223,10000229};
long long fact[MAXN][10],hash1[10],hash2[10],a[MAXN],b[MAXN];
int T,n,m;
int main()
{
for(int i=0;i<10;++i)
{
fact[0][i]=1;
}
for(long long i=1;i<=100000;++i)
{
for(int j=0;j<10;++j)
{
fact[i][j]=(fact[i-1][j]*i)%mod[j];
}
}
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=m;++i)
{
scanf("%lld",&b[i]);
}
for(int i=1;i<10;++i)
{
hash1[i]=hash2[i]=1;
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<10;++j)
{
hash1[j]=(hash1[j]*fact[a[i]][j])%mod[j];
}
}
for(int i=1;i<=m;++i)
{
for(int j=0;j<10;++j)
{
hash2[j]=(hash2[j]*fact[b[i]][j])%mod[j];
}
}
bool flag=true;
for(int j=0;j<10;++j)
{
if(hash1[j]!=hash2[j])
{
flag=false;
}
}
if(flag)
{
printf("equal\n");
}
else
{
printf("unequal\n");
}
}
return 0;
}
题解:差分维护+前缀和(重点如何在一个区间维护不同的值,典型区间修改
先离线,这次利用前缀和与差分数组的关系。
我们知道区间加常数c可以先做差分,此时操作变为单点修改,然后使用前缀和还原。
区间加一次函数bx呢,则可以做二阶差分操作(先差分一次,再对差分数组做一次差分)这时区间加一次函数bx就变为了单点修改,然后做二阶前缀和(前缀和的前缀和)还原,然后这个题就做完了。
区间加二次函数ax^2 以此类推可以做三阶差分,也就是做差分,然后再差分,再差分。最后做三次前缀和还原。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
const long long mod=(long long)1e9+7;
long long d1[MAXN],d2[MAXN],d3[MAXN];//1 id id2
int n,m,T,type,pos;
void pre_sum(long long a[])
{
for(int i=1;i<=n;++i)
{
a[i]=(a[i]+a[i-1])%mod;
}
return;
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(d1,0,sizeof(d1));
memset(d2,0,sizeof(d2));
memset(d3,0,sizeof(d3));
scanf("%d %d",&n,&m);
while(m--)
{
scanf("%d %d",&type,&pos);
if(type==1)
{
d1[pos]++;
}
if(type==2)
{
d2[pos]++;
}
if(type==3)
{
d3[pos]++;
d3[pos+1]++;
}
}
pre_sum(d1);
pre_sum(d2);
pre_sum(d2);
pre_sum(d3);
pre_sum(d3);
pre_sum(d3);
for(int i=1;i<=n;++i)
{
printf("%lld%c",(d1[i]+d2[i]+d3[i])%mod,i==n?'\n':' ');
}
}
return 0;
}
小w的基站网络(未解决)
题解:计算几何+单调队列+dp
标程:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000005;
struct vect
{
long long x;
long long y;
int ki;
}a[MAXN];
bool cmp(const vect &x,const vect &y)
{
long long temp=x.x*y.y-x.y*y.x;
if(temp)
{
return temp>0;
}
else
{
return x.x*x.x+x.y*x.y>y.x*y.x+y.y*y.y;
}
}
long long Cross(vect x,vect y)
{
return x.x*y.y-x.y*y.x;
}
int bgin,s;
long long dp[MAXN];
int stk[MAXN],top,n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%lld %lld",&a[i].x,&a[i].y);
a[i].ki=i;
}
sort(a+1,a+1+n,cmp);
scanf("%d",&s);
for(int i=1;i<=n;++i)
{
if(a[i].ki==s)
{
bgin=i;
break;
}
}
memset(dp,-1,sizeof(dp));
dp[s]=0;
stk[top=1]=bgin;
for(int i=bgin+1;i<=n;++i)
{
if(Cross(a[bgin],a[i])==0)continue;
while(top>1&&Cross(a[stk[top-1]],a[stk[top]])==0)--top;
while(top>1&&dp[a[stk[top]].ki]+Cross(a[stk[top]],a[i])>=dp[a[stk[top-1]].ki]+Cross(a[stk[top-1]],a[i]))--top;
dp[a[i].ki]=dp[a[stk[top]].ki]+Cross(a[stk[top]],a[i]);
stk[++top]=i;
}
for(int i=1;i<=n;++i)
{
printf("%lld\n",dp[i]);
}
return 0;
}
DongDong破密码
题解:直接模拟即可
根据样例分析,所求串为a,密码为b
a1 ^ 0 = b1
a2 ^ a1=b2
a3 ^ a2 ^a1=b3
a4 ^ a3 ^a2=b4
a5 ^ a4 ^a3=b5
a6 ^ a5 ^a4=b6
因此求a串的关系式为:
a1^0 ^0= a1 =b1 ^0
a2 ^a1 ^a1= a2 =b2 ^a1=b2 ^b1
a3 ^ a2 ^a1 ^ a2 ^a1= a3 =b3 ^ a2 ^a1=b3 ^b2
a4 ^ a3 ^a2 ^ a3 ^a2= a4 =b4 ^ a3 ^a2=b4 ^b3 ^a1 (1=4-3)
…等
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std ;
const int N=2e6+10 ;
char s[N] ;
int a[N] , b[N] ;
int n , m ;
int main ()
{
int i ;
scanf(" %d %d %s ",&n,&m,s+1);
for ( i=1 ; i<=n+m-1 ; i++ ) a[i]=(s[i]=='1');
for ( i=1 ; i<=n ; i++ )
if ( i<=m ) b[i]=a[i]^a[i-1];
else b[i]=a[i]^a[i-1]^b[i-m];
for ( i=1 ; i<=n ; i++ ) printf("%d",b[i]);
return 0 ;
}
DongDong跳一跳
题解:dp
一开始想直接暴力求解 但复杂度太大 O(n^2)
然后考虑优化,记录之前达到此高度的柱子所能获得的最大鱼干数量,然后在动态转移 复杂度为 O(n*m) m的值比n小很多,复杂度不高
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=1e7+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
ll a[N],h[N],dp[N],tong[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>h[i]>>a[i];
}
ll ans=0;
for(int i=1;i<=n;i++)
{
ll maxx=0;
for(int j=max(0ll,h[i]-m);j<=h[i]+m;j++)
{
maxx=max(maxx,tong[j]);
}
dp[i]=maxx+a[i],tong[h[i]]=max(tong[h[i]],dp[i]),ans=max(ans,dp[i]);
}
cout<<ans<<endl;
return 0;
}