部分题解(3738,3740,3741,3742,3745),其他题目以后会做了再更新吧。。。
状压dp,如果压20位(10位猫,10位狗),显然复杂度不够。
观察一下发现人和猫有冲突,狗和猫有冲突,人和狗无冲突。那么可以压10位,人选猫,狗选猫,再乘起来就是答案。复杂度O(10*10*2^10)。
转移:
dp[i][1<<j^st]+=dp[i-1][st] (i与j不冲突)
dp[i][st]+=dp[i-1][st]
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 50100
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
const double EPS=1e-9;
/*----------------code-----------------*/
bool r[2][16][16];
ll dp[2][16][1<<10];
int p,c,d,m;
int who(int &x){
int type=0;
x--;
if(x-p>=0) x-=p, type++; else return type;
if(x-c>=0) x-=c, type++;
return type;
}
void DP(int n,bool (*g)[16],ll (*f)[1<<10]){
f[0][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<c);j++){
f[i][j]+=f[i-1][j];
for(int k=0;k<c;k++) if(!(1<<k&j)){
if(g[i-1][k]) continue;
f[i][1<<k^j]+=f[i-1][j];
}
}
}
}
int main(){
while(~scanf("%d%d%d",&p,&c,&d)){
scanf("%d",&m);
CLR(r,0);
CLR(dp,0);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
int u=who(x),v=who(y),op=(u+v)%3;
if(v!=1) swap(x,y);
r[op][x][y]=true;
}
DP(p,r[1],dp[1]); //people choose cat
DP(d,r[0],dp[0]); //dog choose cat
ll ans=0;
for(int i=0;i<(1<<c);i++){
if(__builtin_popcount(i)!=p) continue;
ans+=dp[1][p][i]*dp[0][d][i];
}
printf("%lld\n",ans);
}
return 0;
}
对于每个A[i],有一个区间[ 1-A[i], n-A[i] ]。如果C落在这个区间里,那么A[i]还是符合要求的。问题就变成找一个C,被覆盖的次数最多。
不改变和改变一次都好处理。我们分析一下改变两次怎么求。
绿色线是第一次改变,红色是第二次(i<j)。记cnt[c,i]表示从i到n,c被覆盖的次数。sum[i]表示1到i符合条件的个数。
两次改变后,答案ans=cnt[c1,i]-cnt[c1,j]+cnt[c3,j]+sum[i-1]。
变换下顺序cnt[c1,i]+sum[i-1]+(cnt[c3,j]-cnt[c1,j])
如果i和c1确定了,那么cnt[c1,i]+sum[i-1]是固定部分,我们只要使得Max=(cnt[c3,j]-cnt[c1,j])的值最大化就好,至于j具体在哪里可以不管。
显然Max=max{max{cnt[c,j]} - cnt[c1,j]}(j>i)
从后往前推自然能得到每一个c1对应的Max,cnt[][]也不需要二维了。
维护更新每个c1的Max,用一个数组d[]记录。
复杂度O(n^2)
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 3010
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
const double EPS=1e-9;
/*----------------code-----------------*/
int cnt[N*3],d[N*3],a[N],sum[N];
int main(){
int n,Max,ans;
while(~scanf("%d",&n)){
ans=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(1<=a[i] && a[i]<=n) ans++;
sum[i]=ans;
}
CLR(cnt,0);
CLR(d,0);
Max=0;
for(int i=n;i>=1;i--){
for(int c=1-a[i];c<=n-a[i];c++) Max=max(Max,++cnt[c+n]);
for(int c=-n+1;c<=2*n;c++){
ans=max(ans,cnt[c+n]+sum[i-1]); //change once
ans=max(ans,cnt[c+n]+d[c+n]+sum[i-1]); //change twices
}
for(int c=-n+1;c<=2*n;c++) d[c+n]=max(d[c+n],Max-cnt[c+n]);
}
printf("%d\n",ans);
}
return 0;
}
dp[i]表示前i-1轮已经完成,第i轮可以Level Upper,此时的最大值。用Max[i]表示在[i,i+x+y-1]这段区间内能获得的最大值(使用Level Upper或不使用)。sum[i]表示前i轮能获得的值。
转移:
dp[i]=max{ dp[j]+Max[j]+(sum[i]-sum[j+x+y-1]) | j+x+y-1<i }
所求的答案在dp[n+1],但是n+1这个位置比较特殊,它不需要要求第n+1轮可以Level Upper,因为他根本就不存在。
所以n+1的时候,转移的范围是j<=n
还有一个坑,就是LV5不能增加到LV6
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 100100
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
const double EPS=1e-9;
int a[N],dp[N],Max[N],sum[N];
int main(){
int L,n,x,y;
while(~scanf("%d%d%d%d",&L,&n,&x,&y)){
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
CLR(dp,0);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+(a[i]<=L);
if(L<5) L++;
for(int i=1;i<=n;i++){
int tot=0;
for(int j=i;j<=min(n,i+x-1);j++) if(a[j]<=L) tot++;
for(int j=i+x;j<=min(n,i+x+y-1);j++) if(a[j]<=0) tot++;
Max[i]=max(tot,sum[min(n,i+x+y-1)]-sum[i-1]);
}
for(int i=1;i<=n;i++){
for(int j=1;j+x+y-1<i;j++)
dp[i]=max(dp[i],dp[j]+Max[j]+sum[i-1]-sum[j+x+y-1]);
dp[i]=max(dp[i],sum[i-1]);
}
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,dp[i]+Max[i]+sum[n]-sum[min(n,i+x+y-1)]);
printf("%d\n",ans);
}
return 0;
}
ZOJ 3742 Bellywhite's Algorithm Homework
按点连的边数分为>sqrt(m)的和小于等于sqrt(m)的。
对于小的点,每次更新直接暴力每条边即可。
对于大的点,记录整数和与负数和,每次更新交换一下,同时要更新与它有交集的大的点。也就是说一开始还要预处理出2个大的点之间交集的整数和与负数和。
查询的话,记录一个全局的整数和与负数和,每次都能O(1)回答。
具体更新细节可以看代码。复杂度O(Qsqrt(m))。代码还有可以优化的地方,虽然有重边,但是两点之间可以压缩成两条边,正边,负边。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 50100
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
const double EPS=1e-9;
int n,m,limit,num[N];
ll POS,NEG,pos[N],neg[N];
map<PI,pair<ll,ll> > crs;
vector<int> e[N],big;
struct Edge{
int u,v;
ll val;
}edge[N];
void change(int x){
if(e[x].size()>limit){
for(int i=0;i<big.size();i++){
int u=x,v=big[i];
if(u>v) swap(u,v);
map<PI,pair<ll,ll> >::iterator it;
if((it=crs.find(make_pair(u,v)))!=crs.end()){
v=big[i];
pair<ll,ll> &p=it->second;
pos[v]-=p.first, neg[v]-=p.second;
swap(p.first,p.second);
p.first=-p.first;
p.second=-p.second;
pos[v]+=p.first, neg[v]+=p.second;
}
}
POS-=pos[x], NEG-=neg[x];
swap(pos[x],neg[x]);
pos[x]=-pos[x];
neg[x]=-neg[x];
POS+=pos[x], NEG+=neg[x];
}else{
for(int i=0;i<e[x].size();i++){
int eid=e[x][i];
int u=edge[eid].u, v=edge[eid].v, val=edge[eid].val;
if(u!=x) swap(u,v);
if(e[v].size()>limit){
if(num[u]^num[v]) val=-val;
pos[v]-=val, neg[v]-=val;
}else{
edge[eid].val=-val;
}
POS-=val, NEG-=val;
}
}
num[x]^=1;
}
void pretreat(){
for(int i=1;i<=n;i++) if(e[i].size()>limit){
big.push_back(i);
}
for(int i=0;i<m;i++){
int u=edge[i].u, v=edge[i].v;
if(edge[i].val>0){
pos[u]+=edge[i].val;
pos[v]+=edge[i].val;
}else{
neg[u]+=edge[i].val;
neg[v]+=edge[i].val;
}
if(e[u].size()>limit && e[v].size()>limit){
if(u>v) swap(u,v);
if(edge[i].val>0)
crs[make_pair(u,v)].first+=edge[i].val;
else
crs[make_pair(u,v)].second+=edge[i].val;
}
}
}
void clear(){
big.clear();
crs.clear();
CLR(num,0);
CLR(pos,0);
CLR(neg,0);
for(int i=1;i<=n;i++) e[i].clear();
}
int main(){
int Q,x,Case=0;
while(~scanf("%d%d%d",&n,&m,&Q)){
if(Case++) puts("");
POS=0,NEG=0;
for(int i=0;i<m;i++){
scanf("%d%d%lld",&edge[i].u,&edge[i].v,&edge[i].val);
if(edge[i].val==0){
i--,m--;
continue;
}
e[edge[i].u].push_back(i);
e[edge[i].v].push_back(i);
if(edge[i].val>0) POS+=edge[i].val;
else NEG+=edge[i].val;
}
limit=sqrt(m)+1;
pretreat();
while(Q--){
char op[2];
scanf("%s",op);
if(op[0]=='Q'){
scanf("%s",op);
if(op[0]=='+') printf("%lld\n",POS);
else if(op[0]=='-') printf("%lld\n",NEG);
else printf("%lld\n",POS+NEG);
}else{
scanf("%d",&x);
change(x);
}
if(POS<0 || NEG>0) return -1;
}
clear();
}
return 0;
}
关键信息ri < li+1
那么直接暴力就好了,dp[i]记录i这个数有几个。
如果更新后的数字小于等于ri,那么这个数字就固定了,答案就加上它。
不过代码里我是全部更新好最后一起加的。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 100100
#define ll long long
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int INF=0x3fffffff;
const int MOD=1000000007;
const double EPS=1e-9;
int dp[2*N];
int main(){
int n,m,x;
while(~scanf("%d%d",&n,&m)){
CLR(dp,0);
for(int i=0;i<n;i++){
scanf("%d",&x);
dp[x]++;
}
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
for(int i=r;i>=l;i--){
if(i+c>r) dp[i+c]+=dp[i];
else dp[i+c]=dp[i];
dp[i]=0;
}
}
ll ans=0;
for(int i=1;i<2*N;i++) ans+=1ll*dp[i]*i;
printf("%lld\n",ans);
}
return 0;
}