Day1
T1生活大爆炸版剪刀石头布:模拟,水;
T2联合权值:树形DP,水;
T3Flappy Birds:
这道题我当时算时间复杂度算错了,O(nm^2)的时间复杂度给算成O(nm)了,所以根本就没想优化,以后①算时间复杂度的时候要小心一点了。
其实正解也是很简单的,只是在直译式DP的基础上做了一点小优化。
直译式DP:设f(i,j)为到达当前点的最小步数,则
f(i,j)->f(i-1,j+Y[i-1])
->f(i-1,j-k*X[i-1])+k,k>0且j-k*X[i-1]>0
完全背包式优化:
f(i,j)->f(i-1,j-X[i-1])+1
->f(i,j-X[i-1])+1
->f(i-1,j+Y[i-1])
(顺序不能颠倒)
“因为f(i,j)这个状态实际上可以由f(i,j-X[i-1])这个状态转移而来"——GTY orz
②这种多步与一步等价的思想实际上是非常实用和需要我掌握的。
回来以后写的AC代码:
#include
using namespace std;
#include
#include
#include
#include
typedef short hd;
char * ptr=(char *)malloc(1000000);
int f[10001][1001];
inline void in(hd &x){
while(*ptr<'0'||*ptr>'9')++ptr;
x=0;
while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
int main(){
hd P[10000],down[10001],up[10001],X[10000],Y[10000],N,M,K,tmp,i,j;
int MAXN=2000000000;
freopen("birda.in","r",stdin);
freopen("birda.out","w",stdout);
//读入
fread(ptr,1,1000000,stdin);
in(N),in(M),in(K);
for(i=0;iY[i-1];--j)f[i][j-Y[i-1]]=min(f[i][j-Y[i-1]],f[i-1][j]);
for(j=0;j<=down[i];++j)f[i][j]=MAXN;
for(j=up[i];j<=M;++j)f[i][j]=MAXN;
//判断当前列是否可达
j=1;
while(j<=M&&f[i][j]>=MAXN)++j;
if(j>M)break;
}
if(i>N)printf("1\n%d",*min_element(f[N]+1,f[N+1]));
else printf("0\n%d",lower_bound(P,P+K,i)-P);
}
Day2
T1无线网络发射器选址:当时蛋疼写了个DP,实际上暴力就可以,水。
T2寻找道路:我发现D2好像一直很蛋疼,又写了个DFS+SPFA,实际上DFS+BFS就可以了。
T3:解方程:
这是一道很奇怪的题,我刚看到他的时候没有一点思路。。除了暴力。然后就想写个50分的压位高精暴力,结果还写残了。。所以只得了30分。回来的路上听到学长们讨论各种取模+乱搞。直接取模+暴力的话,O(nm)的时间复杂度实际上是可以得70分的。
那么就来说一下正确解法吧。——来自,各种大神的题解。
引理:若f(x)≡0(mod p),则f(x+p)≡0(mod p).
所以,我们可以先取一个小质数P,求出[1,P)内所有的解,然后可以通过+P推得在[1,m]内所有符合f(x)≡0(mod P),再用另一个大质数p,用来check每个解即可。
时间复杂度的话,应该是难以估计的,因为模质数本身就是一个用正确率换时间的做法嘛。。对于普通一点的数据的话,应该是可以达到O(n*p+n*n*m/p)。我们发现这玩意儿竟然是我刚学的对勾函数。。当p最接近(n*m)^(1/2)时,时间复杂度最低。此时O(10^2*10^4+10^4*10^6/10^4)≈O(2*10^6).
代码:
#include
using namespace std;
#include
#include
#include
#include
int main(){
const int P=10007,p=100000009;
int i,j,m,n,a[101]={0},b[101]={0},mi[101],ans[101],tmp,lst[10008],h=0,t=0;
ans[0]=1,mi[0]=1;
freopen("equationa.in","r",stdin);
freopen("equationa.out","w",stdout);
char * ptr=(char *)malloc(2000000);
scanf("%d%d",&n,&m);
fread(ptr,1,2000000,stdin);
for(i=0;i<=n;++i){
bool flag=0;
while(*ptr<'0'||*ptr>'9')
if(*ptr++=='-')
flag=1;
while(*ptr>47&&*ptr<58){
a[i]=(a[i]*10+*ptr-'0')%P;
b[i]=(b[i]*10+*ptr++-'0')%p;
}
if(flag){
a[i]=-a[i];
b[i]=-b[i];
}
}
for(i=1;i
③这道题应该说让我对hash有了全新的认识。其优化暴力的方法也是很巧妙的,将其花费n*p的时间复杂度模一个小质数之后,我们会发现我们可以判定在(p,m]的范围内有些值是必定不可取的了,即若f(x)≠0(mod p),f(x+p)≠0(mod p),这样便达到了对暴力的优化。