第1题:构造序列
题意:构造一个长度为n的序列,每个元素的范围是[1,k];
对于相邻的两个元素A,B(A在前,B在后),满足A<=B 或者A%B!=0
求方案数,对1e9+7求余。(个人觉得原题意已经很精辟了)
数据范围:
80%,n<=10,k<=1000;
100%,n<=10,k<=10^5;
思路:今天一看第1题有点小懵逼,以往的第1题都是一看就ac的(自我水平还有待提高),然而确实不会难呀(小c)。
P80:dp,dp[i][j]表示第i为j的方案。O(n*k^2);(当时也是急了,mod都忘了,今天也就跪在了这里…)
P100:前缀和维护上一层的方案,仔细分析题目条件A<=B很容易no problem,A%B!=0 实际上就是去找B的倍数!(赛后一切都是屁话)
所以先加上所有的方案,再减掉它的倍数的方案。(注意过程要mod呀!)
#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define LL long long
#define N 15
#define M 100005
using namespace std;
const int P=1e9+7;
int n,K;
LL ans;
LL dp[N][M];
struct P80{
void solve(){
REP(i,1,n-1){
REP(j,1,K){
REP(k,1,K){
if(k>=j || j%k!=0)dp[i+1][k]+=dp[i][j],dp[i+1][k]%=P;
}
}
}
REP(i,1,K)ans+=dp[n][i],ans%=P;
cout<<ans<<endl;
}
}p80;
struct P100{
int sum[M];
void solve(){
REP(i,2,K){
sum[i]=K;
for(int j=2;j*i<=K;j++)sum[i]-=dp[1][i*j];
}
REP(i,2,n){
int tmp=1;
REP(j,2,K){
dp[i][j]+=sum[j],dp[i][j]%=P;
tmp+=dp[i][j],tmp%=P;
}
REP(j,1,K){
sum[j]=tmp;
for(int k=2;k*j<=K;k++)sum[j]-=dp[i][j*k],sum[j]=(sum[j]+P)%P;//-
}
}
LL ans=1;
REP(i,2,K)ans+=dp[n][i],ans%=P;
cout<<ans<<endl;
}
}p100;
int main(){
scanf("%d%d",&n,&K);
REP(i,1,K)dp[1][i]=1;
if(K<=1000)p80.solve();
else p100.solve();
return 0;
}
第2题:1/2背包
题意:体积m,n个物品(cost,v),与普通的背包问题类似,但此题的cost=1 or 2。(这也成就了数据的范围和思路)
数据范围:
60%,n<=2000,m<=10^4;
100%,n<=2*10^5,m<=5*10^5,v<=10^4;
思路:
P60:普通的01背包,这里不解释了;
P100:由于此题的cost=1 or 2 ,给人的感觉就是可以贪心,按性价比去排序,(你说你不造性价比是什么,那就很尴尬了)。
第3题:引水入城
题意:n*m的矩形城市,第1行的城市濒临湖泊,可以蓄水,建造蓄水厂,为相邻的城市传输水,然而两个城市传输水的条件是自然法则(水从高处流,自然要海拔差);第n行的城市靠近沙漠,为干旱区,需水源。所以问题来了,能否满足所有干旱区的城市都有水,(0为否,1为是),若满足,则输出最小要建造的蓄水厂数,否则,输出干旱区有多少城市缺水。
数据范围:
100%:n,m<=500
思路:
P60:听说暴力就60(不好意思,今天想到正解了);
P100:此题的问法,显然分2个步骤。1,判断是否满足要求,这里bfs就行了;2,不满足的话好说,可以直接累计mark[n][1~m],而满足的话,就再dfs,记录每个蓄水厂到第n行的[L,R],且这里蓄水厂所到达的区间都不会交集(据题意,也是很好证明的),那就dp求出这个最小值,(也是一开始前2题都是dp的错觉)。
#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define LL long long
#define INF 0x3f3f3f3f
#define N 505
using namespace std;
int n,m,ans;
int h[N][N];
bool mark[N][N];
struct node{
int x,y;
};
queue<node>Q;
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
void Rd(){
scanf("%d%d",&n,&m);
REP(i,1,n)REP(j,1,m)scanf("%d",&h[i][j]);
}
bool check(int x,int y){return !x || x>n || !y || y>m || mark[x][y];}
struct P100{
void bfs(){
REP(i,1,m)mark[1][i]=1,Q.push((node){1,i});
while(!Q.empty()){
node now=Q.front();Q.pop();
REP(i,0,3){
int nx=now.x+dx[i],ny=now.y+dy[i];
if(check(nx,ny))continue;
if(h[now.x][now.y]<=h[nx][ny])continue;
mark[nx][ny]=1;
Q.push((node){nx,ny});
}
}
}
struct city{
int L,R;
}C[N];
int now_id;
int dp[N];
void dfs(node now){
mark[now.x][now.y]=1;
if(now.x==n){
C[now_id].L=min(C[now_id].L,now.y);
C[now_id].R=max(C[now_id].R,now.y);
}
REP(i,0,3){
int nx=now.x+dx[i],ny=now.y+dy[i];
if(check(nx,ny))continue;
if(h[now.x][now.y]<=h[nx][ny])continue;
if(!mark[nx][ny])dfs((node){nx,ny});
}
}
void solve(){
bfs();
REP(i,1,m)if(!mark[n][i])ans++;
if(ans>0){
printf("0\n%d\n",ans);
exit(0);
}
REP(i,1,m){
memset(mark,0,sizeof(mark));
C[i]=(city){m+1,0};
now_id=i;
dfs((node){1,i});
}
ans=INF;
memset(dp,INF,sizeof(dp));
dp[0]=0;
REP(i,1,m){
REP(j,1,m){
if(i<C[j].L || i>C[j].R)continue;
dp[i]=min(dp[i],dp[C[j].L-1]+1);
}
}
printf("1\n%d\n",dp[m]);
}
}p100;
int main(){
Rd();
p100.solve();
return 0;
}
小结:
这次虽然考得不错,每题都做到了且分,但没有mod,还有犯如此低级的错误,也有什么可说的。下次直接在define下面打上(LL,mod,内存)。