A
签到
#include <bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
long long q,k;
long long dp[100003][2];
int main(){
scanf("%lld%lld",&q,&k);
dp[0][0]=1;
for(long long i=1;i<=100000;i++){
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%mod;
if(i>=k)dp[i][1]=dp[i-k][0];
}
for(long long i=1;i<=100000;i++){
dp[i][0]=(dp[i][0]+dp[i-1][0])%mod;
dp[i][1]=(dp[i][1]+dp[i-1][1])%mod;
}
while(q--){
long long l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",(dp[r][0]-dp[l-1][0]+dp[r][1]-dp[l-1][1]+2*mod)%mod);
}
}
C
首先把问题转化为平面上有n个点,有m个询问,每个询问给出一个点,查询该点与n个点连线的斜率的最小值。离线询问,考虑每个点左边的点的贡献,发现只有在上凸壳上的点才有可能取到最小值,用一个栈维护上凸壳。并且发现其实查询时如果假装把查询的点插入凸包,把凹进去的点都出栈后,和栈顶的点的连线的斜率就是最小值,于是我们二分这个栈顶下标即可。注意使用叉积而不是斜率大小作为出入栈判断。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
struct node{
ll x,y,id;
void print(){
cout<<id<<' '<<x<<' '<<y<<endl;
}
double u(){
return -1.0*y/x;
}
}rec[100003];
ll tot=0;
node operator-(const node& a,const node& b){
return (node){a.x-b.x,a.y-b.y,0};
}
ll operator*(const node& a,const node& b){
return a.x*b.y-a.y*b.x;
}
bool operator<(const node& a,const node& b){
return a.x<b.x;
}
node st[50003];
ll top;
double ans[50003];
bool check(int last,node p){
if(last<=0)return true;
return (p-st[last-1])*(st[last]-st[last-1])>0;
}
void insert(node p){
while(!check(top-1,p))top--;
st[top++]=p;
}
double query(node p){
if(top==0)return 0;
int l=0,r=top-1;
while(1)
{
if(r-l<=1){
if(check(r,p))return (p-st[r]).u();
else return (p-st[l]).u();
}
int mid=(l+r)/2;
if(check(mid,p))l=mid;
else r=mid;
}
}
void work(){
top=0;
for(ll i=1;i<=tot;i++){
//rec[i].print();
if(rec[i].id==0){
insert(rec[i]);
}
else ans[rec[i].id]=max(ans[rec[i].id],query(rec[i]));
}
}
int main(){
memset(ans,0,sizeof(ans));
scanf("%lld",&n);
for(ll i=1;i<=n;i++){
ll x,y;
scanf("%lld%lld",&x,&y);
rec[++tot]=(node){x,y,0};
}
scanf("%lld",&m);
for(ll i=1;i<=m;i++){
ll x,y;
scanf("%lld%lld",&x,&y);
rec[++tot]=(node){x,y,i};
}
sort(rec+1,rec+1+tot);
work();
reverse(rec+1,rec+1+tot);
work();
for(ll i=1;i<=m;i++){
if(ans[i]==0){
printf("No cross\n");
}
else printf("%.15f\n",ans[i]);
}
}
D
签到
#include <bits/stdc++.h>
using namespace std;
int t,n,a[100007],py;
long long ans,last;
int main (){
cin>>t;
while (t--){
scanf ("%d",&n);
for (int i=1;i<=n;i++){
scanf ("%d",&a[i]);
// cout<<a[i]<<' ';
}
ans=py=0;
last=a[1];
a[n+1]=-1;
for (int i=2;i<=n+1;i++){
if (a[i]<a[i-1]){
if (a[i-1]>last){
ans+=(long long)a[i-1]-last;
py+=2;
}
last=a[i];
}
}
cout<<ans<<' '<<py<<endl;
}
return 0;
}
G
首先枚举汇集的位置,二分长度,考虑在长度以内的都移到中心,判断价值是否足够,这个是两个log的。考虑把二分长度变成二分右端点,又发现在中点递增的情况下,右端点若减对答案没有贡献,于是右端点是递增的,成功去掉一个log。
#include <bits/stdc++.h>
using namespace std;
typedef long long ull;
int l[500007],R,n,a[500007],x[500007];
ull T,sa[500007],sax[500007],tans,ans,tmp;
inline int half (int l,int r,int xm,int lim){
if (l>=r)return l;
int mid=(l+r)>>1;
if (xm-x[mid]<=lim)return half (l,mid,xm,lim);
else return half (mid+1,r,xm,lim);
}
inline int half2 (int l,int r,int mid){
if (l>=r)return l;
int L=(l+r)>>1;
ull tmp=(sax[R]-sax[mid])-(sa[R]-sa[mid])*(ull)x[mid];
tmp+=(sa[mid]-sa[L-1])*(ull)x[mid]-(sax[mid]-sax[L-1]);
if (tmp<=T)return half2 (l,L,mid);
return half2 (L+1,r,mid);
}
inline int pd (int mid,int r){
int L;
L=half (1,mid,x[mid],x[r]-x[mid]);//x[mid]-x[l]<x[r]-x[mid]
ull tmp=(sax[R]-sax[mid])-(sa[R]-sa[mid])*(ull)x[mid];
tmp+=(sa[mid]-sa[L-1])*(ull)x[mid]-(sax[mid]-sax[L-1]);
if (tmp<=T)return L;
return -L;
}
inline void fig (int mid,int L){
ull tmp=(sax[R]-sax[mid])-(sa[R]-sa[mid])*(ull)x[mid];
tmp+=(sa[mid]-sa[L-1])*(ull)x[mid]-(sax[mid]-sax[L-1]);
tans=sa[R]-sa[L-1];
if (L>1){
if (R==n||x[mid]-x[L-1]<=x[R+1]-x[mid]){
tans+=(T-tmp)/(ull)(x[mid]-x[L-1]);
}
else tans+=(T-tmp)/(ull)(x[R+1]-x[mid]);
}
else{
if (R<n)tans+=(T-tmp)/(ull)(x[R+1]-x[mid]);
}
ans=max(ans,tans);
}
void work (){
R=1;
ans=tans=0;
for (int i=1;i<=n;i++){
while (R<=n){
l[R]=pd (i,R);
if (l[R]<0)break;
R++;
}
R--;
if (R==n)l[R+1]=1;
else l[R+1]=-l[R+1];
int L=half2 (l[R+1],l[R],i);
fig (i,L);
}
cout<<ans<<endl;
}
int main (){
cin>>n>>T;
T/=2;
for (int i=1;i<=n;i++){
scanf ("%d",&x[i]);
}
sa[0]=sax[0]=0;
for (int i=1;i<=n;i++){
scanf ("%d",&a[i]);
sa[i]=sa[i-1]+(ull)a[i];
sax[i]=sax[i-1]+(ull)a[i]*(ull)x[i];
}
work ();
return 0;
}
H
使用树形DP。
f[i][j] 表示以 i 为根的子树选了 j 条路径的最大权值和。
g[i][j] 表示以 i 为根的子树选了 j 条路径加一条包含 i 的竖直链的最大权值和。
共有三种更新:
儿子取2个 f 加上根节点更新父节点的 g
儿子取1个 f 加上根节点更新父节点的 f
儿子取0个 f 不加上根节点更新父节点的 g
用类似背包的做法完成单节点上的dp即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline void gmax(ll &a,ll b){
a=max(a,b);
}
vector<int>G[400003];
int x[400003];
ll f[400003][4];
ll g[400003][4];
int n;
void dfs(int u,int fa){
ll a[4][3]={0};
ll b[4][3]={0};
ll c[4][3]={0};
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v==fa)continue;
dfs(v,u);
memset(b,0,sizeof(b));
for(int i=0;i<4;i++){
for(int j=0;j<2;j++){
for(int k=1;k<=3;k++){
if(i==0&&j)continue;
int it=i+k;
int jt=j+1;
if(jt==2)it--;
if(it>3)continue;
gmax(b[it][jt],a[i][j]+f[v][k]);
}
}
}
memset(c,0,sizeof(c));
for(int i=0;i<4;i++){
for(int j=0;j<=2;j++){
for(int k=1;k<=3;k++){
if(i==0&&j)continue;
int it=i+k;
if(it>3)continue;
gmax(c[it][j],a[i][j]+g[v][k]);
}
}
}
for(int i=0;i<4;i++){
for(int j=0;j<3;j++){
gmax(a[i][j],b[i][j]);
gmax(a[i][j],c[i][j]);
}
}
}
for(int i=1;i<4;i++)f[u][i]=a[i][1]+x[u];
for(int i=1;i<4;i++)g[u][i]=a[i][0];
for(int i=1;i<4;i++)gmax(g[u][i],a[i][2]+x[u]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&x[i]);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
printf("%lld\n",g[1][3]);
}
I
考虑没有障碍的情况,注意到4个角上最多有4个贡献,把角刨除之后问题规模就变成了n-2,可以递归解决。进一步发现其实只有对角线上的点有贡献,规模是
o(n)
o
(
n
)
的,每个障碍把和它同一行的横向行走方案去除,把和它同一列的纵向行走方案去除。把所有对角线上的点每四个分一组,暴力枚举行走方案即可。
#include <bits/stdc++.h>
using namespace std;
int n,m,a[5],b[5],ans;
bool fx[100007],fy[100007],h[5],s[5];
void work (){
int i,j;
for (i=1;i<=n/2;i++){
a[1]=i;b[1]=i;
a[2]=i;b[2]=n-i+1;
a[3]=n-i+1;b[3]=i;
a[4]=n-i+1;b[4]=n-i+1;
for (j=1;j<=4;j++){
h[j]=fx[a[j]];
s[j]=fy[b[j]];
// cout<<j<<' '<<h[j]<<' '<<s[j]<<endl;
}
int tmp1=(!h[1])+(!s[2])+(!s[3])+(!h[4]);
int tmp2=(!s[1])+(!h[2])+(!h[3])+(!s[4]);
// cout<<i<<' '<<max(tmp1,tmp2)<<endl;
// if (max(tmp1,tmp)>2)
ans+=max(tmp1,tmp2);
}
if (n%2==1){
int tx=n/2+1;
// cout<<tx<<' '<<s
if (fx[tx]==0||fy[tx]==0)ans++;
}
printf("%d\n",ans);
}
int main (){
int x,y;
while(~scanf ("%d%d",&n,&m)){
memset (fx,0,sizeof (fx));
memset (fy,0,sizeof (fy));
ans=0;
for (int i=1;i<=m;i++){
scanf ("%d%d",&x,&y);
fx[x]=1;
fy[y]=1;
}
work();
}
return 0;
}
J
每个点维护施肥次数
C0
C
0
、施肥种类和
C1
C
1
、施肥种类平方和
C2
C
2
,一个种类为
C
C
的点活到最后当且仅当且
C0∗C2=C2
C
0
∗
C
2
=
C
2
。这个可以二维前缀和
O(n∗m)
O
(
n
∗
m
)
,我写的多了一个log。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
ll c0[1000003];
ll c1[1000003];
ll c2[1000003];
inline int lowbit(int x){
return x&(-x);
}
void update(int x,int col,int op){
while(x<=1000000){
c0[x]+=op;
c1[x]+=op*col;
c2[x]+=1ll*op*col*col;
x+=lowbit(x);
}
}
bool query(int x,int col){
ll s0=0,s1=0,s2=0;
while(x){
s0+=c0[x];
s1+=c1[x];
s2+=c2[x];
x-=lowbit(x);
}
if(s0*col==s1&&s0*col*col==s2)return false;
return true;
}
int n,m,q;
struct que{
int x,y,col,typ;
}rec[5000003];
bool operator<(const que& a,const que& b){
if(a.x==b.x){
return abs(a.typ)>abs(b.typ);
}
return a.x<b.x;
}
int tot=0;
int ans=0;
int main(){
n=read();
m=read();
q=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int col=read();
rec[++tot]=(que){i,j,col,0};
}
}
int xl,yl,xr,yr,col;
for(int i=1;i<=q;i++){
xl=read();
yl=read();
xr=read();
yr=read();
col=read();
rec[++tot]=(que){xl,yl,col,1};
rec[++tot]=(que){xl,yr+1,col,-1};
rec[++tot]=(que){xr+1,yl,col,-1};
rec[++tot]=(que){xr+1,yr+1,col,1};
}
sort(rec+1,rec+1+tot);
for(int i=1;i<=tot;i++){
if(rec[i].typ==0){
if(query(rec[i].y,rec[i].col)){
ans++;
}
}
else {
update(rec[i].y,rec[i].col,rec[i].typ);
}
}
printf("%d\n",ans);
}
K
把一行字符串hash成一个ll,做一遍kmp得到p,列方向再做一遍得到q。
所有大小为p*q的子矩阵最大值的最小值做两遍经典的窗口单调队列即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=9999999999999937ll;
ll gethash(string s){
int len=s.length();
ll ret=0;
for(int i=0;i<len;i++){
ret*=131;
ret+=s[i]-'a'+3;
ret%=mod;
}
return ret;
}
int a[1000003];
vector<int>mx[1000003];
vector<string>s;
char tmp[1000003];
int n,m;
ll s1[1000003];
int f1[1000003];
ll s2[1000003];
int f2[1000003];
int calc(ll s[],int f[],int n){
f[1]=0;
for(int i=2;i<=n;i++){
int j=f[i-1];
while(j&&s[j+1]!=s[i])j=f[j];
if(s[j+1]==s[i])j++;
f[i]=j;
}
return n-f[n];
}
int que[1000003];
int head,tail;
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
scanf("%s",tmp);
s.push_back(tmp);
s1[i+1]=gethash(tmp);
}
for(int j=0;j<m;j++){
string p="";
for(int i=0;i<n;i++)p+=s[i][j];
s2[j+1]=gethash(p);
}
int p=calc(s1,f1,n);
int q=calc(s2,f2,m);
for(int i=0;i<n;i++){
head=1,tail=0;
for(int j=0;j<m;j++){
scanf("%d",&a[j]);
if(que[head]==j-q)head++;
while(head<=tail&&a[que[tail]]<=a[j])tail--;
que[++tail]=j;
if(j>=q-1)mx[i].push_back(a[que[head]]);
}
}
int ans=1e9+7;
for(int j=0;j<m-q+1;j++){
head=1,tail=0;
for(int i=0;i<n;i++){
a[i]=mx[i][j];
if(que[head]==i-p)head++;
while(head<=tail&&a[que[tail]]<=a[i])tail--;
que[++tail]=i;
if(i>=p-1)ans=min(ans,a[que[head]]);
}
}
printf("%lld\n",1ll*ans*(p+1)*(q+1));
}