很简单的一个模拟算法。
从0标号就可以用模,从1标号也可以通过特判(>n就减小于1就加)
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=100000+10;
char s[maxn][20];
int a[maxn],b[maxn];
int i,j,k,l,t,n,m,ans;
char ch;
int read(){//if read a big number,need ll
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
char get(){
char ch=getchar();
while (ch<'a'||ch>'z') ch=getchar();//zi fu chuan jin bao han a-z
return ch;
}
int main(){
freopen("toy.in","r",stdin);freopen("toy.out","w",stdout);
n=read();m=read();
fo(i,1,n){
a[i]=read();
b[i]=1;
s[i][1]=get();
do{
ch=getchar();
if (ch<'a'||ch>'z') break;
s[i][++b[i]]=ch;
}while (1);
}
ans=1;
fo(i,1,m){
j=read();k=read();
if (j==a[ans]) t=1;else t=0;//t biao shi shun hai shi ni
if (t){
ans-=k;
if (ans<=0) ans+=n;
}
else{
ans+=k;
if (ans>n) ans-=n;
}
}
fo(i,1,b[ans]) printf("%c",s[ans][i]);
printf("\n");
fclose(stdin);fclose(stdout);
return 0;
}
我们用d表示深度,a表示一条路线的长度。
对于路线S->T,若lca为V,拆成S->V以及V->T两条树链。
对于S->V上每一个点j,其答案可以加一的条件:
dS−dj=wj
dS=dj+wj
右边只与j有关。
同样,对于V->T上每一个点j,其答案可以加一的条件:
dT−dj=ai−wj
dT−ai=dj−wj
右边只与j有关。
因此思考离线做法,对于一条路线,在S和T分别打两个tag,并在lca即V处回收tag。
用两颗线段树维护子树内目前还没被撤销掉的dS和dT-ai的权值线段树,权值可能为负数需要设置一个绝对值的最大值来加上,或者提前进行离散化。
这个线段树是单点修改和查询的,用线段树仅仅是因为线段树可以进行合并。
这个算法带log,如何线性知道一个子树内某权值的数量?
维护一个桶,进入一个节点时得到该节点子树所需权值的数量t1,退出该节点时在桶中得到该节点子树所需权值的数量t2,那么显然该子树内有t2-t1个所需权值。
如果我们用2^n枚举每节课申请或不申请,那么我们需要知道如何计算期望。
给你一个DAG,入度为0的点只有一个,每条边有两个权值a和b,从入度为0的点走到另一点所有的路径中a的和乘b的积的和,这其实就是期望的模型。
已知一个点的E和P( E=∑(V∗P) ,这里的V和P表示一条路径上的权值和与概率积),现在该点有一条出边(a,b),对该边的终点的E和P的影响是?
对P的影响显然是 P∗b ,加上这个即可。
E?
∑(V+a)∗P∗b
E∗b+P∗a∗b
用这个方法递推即可。
那么影响答案的就是我们的决策,也就是是否申请。
设f[i,j,0~1]表示上完第i节课,申请了j次,第i节课是否申请的最小期望。
以申请了为例,假如决策时下一节课也申请。根据上面的式子,你会发现当前的概率与前面的决策均无关,因为如果申请了,那么就有p[i]的概率在d[i],有1-p[i]的概率在c[i]。因此可以进行转移。
上面的讲法可能稍微复杂,其实知道期望的线性性也可以推一下。
预处理一个阶乘包含了多少个某质数。
然后求一个组合数是否是k的倍数可以分解质因数。
最后再根据组合数是否为k的倍数产生的01贡献矩阵做二维前缀和,可以o(1)回答每个询问。
这个方法不知道能不能跑过老爷机。
实际上,可以用组合数的公式
Cji=Cji−1+Cj−1i−1
n^2的在模k意义下求组合数是否为k的倍数。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=2000+10;
int a[maxn][maxn],c[maxn][maxn],sum[maxn][maxn];
int i,j,k,l,t,n,m,ca,ans;
int read(){//if read a big number,gai ll
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int main(){
freopen("problem.in","r",stdin);freopen("problem.out","w",stdout);
ca=read();k=read();
c[0][0]=1;
fo(i,1,2000){
c[i][0]=1;
fo(j,1,i)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;
}
fo(i,0,2000)
fo(j,0,2000){
if (i<j) a[i][j]=0;
else a[i][j]=(c[i][j]==0);
}
fo(i,0,2000)
fo(j,0,2000){
sum[i][j]=a[i][j];
if (i>0) t=sum[i-1][j];else t=0;
sum[i][j]+=t;
if (j>0) t=sum[i][j-1];else t=0;
sum[i][j]+=t;
if (i>0&&j>0) t=sum[i-1][j-1];else t=0;
sum[i][j]-=t;
}
while (ca--){
n=read();m=read();
ans=sum[n][m];
printf("%d\n",ans);
}
fclose(stdin);fclose(stdout);
return 0;
}
结论一:长的一定先切。
证明:显然。
结论二:对于相邻两次切的长度i和j(i>=j),切出来的ip和jp,将来一定是先切ip再切jp。
证明:
假如在t时刻切i,t+1时刻切j,那么从t+2时刻开始两者增长速度一致,因此可以只探究两者在t+1时刻的长度。
前者: ⌊ip⌋+q=⌊ip+q⌋
后者: ⌊(j+q)p⌋=⌊jp+qp⌋
显然前者大于后者。
再根据结论一,前者会先切。
根据结论二可以类推出i-ip比j-jp先切。
因此开三个队列维护。
第一个队列维护排序后的原队列。
第二个队列维护切出来的乘p。
第三个队列维护切出来的乘1-p。
每次从三个队列队首取一个最大的就是要切的(结论一),然后切出的两份分别加入后两个队列的队尾(结论二)
这是线性的。
整体+q的影响可以考虑变成单个-q。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const ll inf=10000000000000000;
const int maxn=8000000+10;
int sta[100];
ll a[maxn],b[maxn],c[maxn],k,l,t,ans,x,y;
int i,j,n,m,u,v,tot,top,q,tt,root,h1,e1,h2,e2,h3,e3;
db p;
int read(){//if read a big number,gai ll
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void write(ll x){
if (!x){
putchar('0');
putchar(' ');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top){
putchar('0'+sta[top]);
top--;
}
putchar(' ');
}
bool cmp(int a,int b){
return a>b;
}
int main(){
//freopen("earthworm.in","r",stdin);freopen("earthworm.out","w",stdout);
n=read();m=read();q=read();u=read();v=read();tt=read();
p=(db)u/v;
fo(i,1,n) a[i]=read();
sort(a+1,a+n+1,cmp);
h1=h2=h3=1;e1=n;
fo(i,1,m){
if (h1<=e1) k=a[h1];else k=-inf;
if (h2<=e2) l=b[h2];else l=-inf;
if (h3<=e3) t=c[h3];else t=-inf;
if (k>=l&&k>=t) ans=k,h1++;
else if (l>=k&&l>=t) ans=l,h2++;
else ans=t,h3++;
x=floor((ans+(ll)(i-1)*q)*p);y=ans+(ll)(i-1)*q-x;
b[++e2]=x-(ll)i*q;
c[++e3]=y-(ll)i*q;
if (i%tt==0) write(ans+(ll)(i-1)*q);
}
printf("\n");
fo(i,1,n+m){
if (h1<=e1) k=a[h1];else k=-inf;
if (h2<=e2) l=b[h2];else l=-inf;
if (h3<=e3) t=c[h3];else t=-inf;
if (k>=l&&k>=t) ans=k,h1++;
else if (l>=k&&l>=t) ans=l,h2++;
else ans=t,h3++;
if (i%tt==0) write(ans+(ll)m*q);
}
printf("\n");
fclose(stdin);fclose(stdout);
return 0;
}
三点确定一条抛物线(注意三点共线以及两点横坐标相同的情况)
状压DP,每次枚举两只猪确定发射一只鸟的抛物线(也可以只射一只猪,注意合法抛物线必须a<0)
这样是2^n*n*n。
注意标号最小的猪一定要死,每次可以解决最小的猪,只枚举另一只猪来确定抛物线。
2^n*n。
搜索+最优性剪枝+记忆化也可以。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=18+10;
const db eps=0.000000001;
db dx[maxn],dy[maxn];
int bz[maxn];
int f[(1<<19)+50];
bool vis[(1<<19)+50];
int i,j,k,l,t,n,m,ans,ca;
int read(){//if read a big number,gai ll
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void dfs(int x,int y,int z){
if (vis[z]){
if (y+f[z]<ans) ans=y+f[z];
return;
}
if (y>=ans) return;
if (x==n+1){
f[z]=0;
ans=y;
return;
}
if ((z&(1<<(x-1)))==0){
dfs(x+1,y,z);
return;
}
int i,j,t;
bool pd[maxn];
db a,b;
fo(i,x,n) pd[i]=0;
fo(i,x+1,n)
if (!pd[i]&&(z&(1<<(i-1)))){
pd[i]=1;
t=0;
if (dx[i]==dx[x]) continue;
if (dy[i]/dx[i]==dy[x]/dx[x]) continue;
b=(dy[i]*dx[x]*dx[x]-dy[x]*dx[i]*dx[i])/(dx[x]*dx[x]*dx[i]-dx[i]*dx[i]*dx[x]);
a=(dy[i]-b*dx[i])/(dx[i]*dx[i]);
if (a>=0) continue;
t+=(1<<(x-1));t+=(1<<(i-1));
fo(j,i+1,n)
if (fabs(dy[j]-dx[j]*dx[j]*a-dx[j]*b)<eps){
if (z&(1<<(j-1))) t+=(1<<(j-1));
pd[j]=1;
}
dfs(x+1,y+1,z-t);
if (f[z-t]+1<f[z]) f[z]=f[z-t]+1;
}
dfs(x+1,y+1,z-(1<<(x-1)));
if (f[z-(1<<(x-1))]+1<f[z]) f[z]=f[z-(1<<(x-1))]+1;
vis[z]=1;
}
int main(){
freopen("angrybirds.in","r",stdin);freopen("angrybirds.out","w",stdout);
ca=read();
while (ca--){
n=read();m=read();
fo(i,0,(1<<n)-1) f[i]=n+1,vis[i]=0;
fo(i,1,n) scanf("%lf%lf",&dx[i],&dy[i]);
ans=n;
dfs(1,0,(1<<n)-1);
printf("%d\n",ans);
}
fclose(stdin);fclose(stdout);
return 0;
}