test20181021-day1(九校聯考)
阿新 • • 發佈:2018-11-08
題解尚未出,先口胡一波
T1
讀題,曰:"普及組裸題."大喜,遂敲完全揹包,小樣例全然無異,心得意滿,以為A之
然後...
這...n,m巨大,貌似不可做.但此題有一個突出的性質,\(a,b \leq 100\).於是欲以其做文章.去重後其實只有10000個物品,這樣有40分...
然後又根據一種神奇的思想: 如果一個選手比你小還比你強,你就可以退役了 ,可以刪去許多無用的物品.
考場上寫了個二維樹狀陣列亂搞,但後來學長告訴我只能刪體積有倍數關係的一些點.
在我的亂搞冷靜分析後,物品數n其實已經比較小了,但是m仍舊很大.乃口胡一性質,貪心放入物品直至m降到1e5,再跑完全揹包..
這樣的正確性應該是得不到保證的,但好像資料比較水,就,就A了..
#include<bits/stdc++.h> #define ll long long #define lowbit(x) x&(-x) using namespace std; inline int read() { int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') { fh=-1; jp=getchar(); } while (jp>='0'&&jp<='9') { out=out*10+jp-'0'; jp=getchar(); } return out*fh; } const int MAXN=1e6+10; int n,len=0; ll m,ans=0; bool bit[250][250]; ll f[101000]={0}; inline void add(int x,int y) { for(int i=x;i<=101;i+=lowbit(i)) for(int j=y;j<=100;j+=lowbit(j)) bit[i][j]=1; } inline int sum(int x,int y) { int s=0; for(int i=x;i;i-=lowbit(i)) for(int j=y;j;j-=lowbit(j)) s+=bit[i][j]; return s; } struct node{ int a,b;//體積,價值 bool operator == (const node& rhs) const { return (a==rhs.a && b==rhs.b); } bool operator < (const node& rhs) const { return (b*rhs.a>a*rhs.b); } }Q[MAXN],P[MAXN]; int main() { freopen("backpack.in","r",stdin); freopen("backpack.out","w",stdout); scanf("%d%lld",&n,&m); for(int i=0;i<n;++i) { Q[i].a=read(); Q[i].b=read(); } n=unique(Q,Q+n)-Q; for(int i=0;i<n;++i) add(102-Q[i].b,Q[i].a); for(int i=0;i<n;++i) { int k=sum(102-Q[i].b,Q[i].a); if(k<2) { P[len].a=Q[i].a; P[len].b=Q[i].b; ++len; } } n=len; sort(P,P+n); ll t=max(1ll*0,(m-100000)/P[0].a); m-=1ll*P[0].a*t,ans+=1ll*P[0].b*t; for(int i=0;i<n;++i) for(int j=P[i].a;j<=m;++j) f[j]=max(f[j],f[j-P[i].a]+P[i].b); cout<<f[m]+ans; return 0; }
T2
資料範圍 \(n,m \leq 5e5\),操作保證了不會爆long long.
題面好長啊...
開始沒理解到"這個序列有序",以為是每次修改後每個數的rank不變,卡了好久 ,後來發現就是指這個序列是遞增的...
這樣的話,
最大值是後半段減前半段,顯然用線段樹維護;
最小值是區間中的偶數位置的和減去區間中的奇數位置的和,考試時想了一會,又開了一顆線段樹,維護\(c[2]-c[1],c[4]-c[3]...c[2*n]-c[2*n-1]\)這樣一個類似差分的序列.
修改區間[L,R]時,如果L是奇數,就不用處理;如果是偶數,就要在L/2的位置加上val,在(R+1)/2的位置減去val,類似差分的操作.
回答詢問[L,R]時,如果L是奇數,那麼就直接查詢區間和;如果是偶數,就算出[L+1,R-1]的值,乘上-1,加上R位置的值,再減去L位置的值,這樣調整得到答案.
考完後才知道這做法很sb...直接同時維護區間奇數位置和,偶數位置和,總和,合併的時候注意下就好了...
還好在後面的大力除錯之下,我這sb做法沒有出問題...
至於方案總數,個人覺得是這三個中最難想的...
容易知道方案數只與區間長度有關,看來是一個數學式子...手算了前5項,開始算成了\(1,2,5,15,42\),死活沒看出來...
然後猜到懷疑人生,驗算一波,發現第4項應該是14...
\(1,2,5,14,42\),Catalan數吧...然後怒敲200+行,開始除錯,有驚無險A了此題...
#include<bits/stdc++.h>
#define ll long long
#define root Tree[o]
#define lson Tree[o<<1]
#define rson Tree[o<<1|1]
using namespace std;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
{
fh=-1;
jp=getchar();
}
while (jp>='0'&&jp<='9')
{
out=out*10+jp-'0';
jp=getchar();
}
return out*fh;
}
const int P=1e9+7;
const int MAXN=1e6+10;
inline ll add(ll a,ll b)
{
return (a+b)%P;
}
inline ll mul(ll a,ll b)
{
return (a*b)%P;
}
inline ll sub(ll a,ll b)
{
return (a-b)%P<0? (a-b)%P+P : (a-b)%P;
}
inline ll fpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
res=mul(a,res);
b>>=1;
a=mul(a,a);
}
return res;
}
inline ll inv(ll x)
{
return fpow(x,P-2);
}
struct SegTree{
struct node{
ll val,f;
int l,r;
}Tree[MAXN<<2];
inline void pushup(int o)
{
root.val=add(lson.val,rson.val);
}
void BuildTree(int o,int l,int r,int v[])
{
root.l=l,root.r=r;
root.f=0;
if(l==r)
{
root.val=1ll*v[l];
return;
}
int mid=(l+r)>>1;
BuildTree(o<<1,l,mid,v);
BuildTree(o<<1|1,mid+1,r,v);
pushup(o);
}
void Modifiy(int o,ll c)
{
root.f=add(root.f,c);
root.val=add(root.val,mul(c,root.r-root.l+1));
}
void pushdown(int o)
{
if(root.f)
{
Modifiy(o<<1,root.f);
Modifiy(o<<1|1,root.f);
root.f=0;
}
}
void update_seg(int o,int L,int R,ll c)
{
int l=root.l,r=root.r;
if(r<L || l>R)
return;
if(L<=l && r<=R)
{
Modifiy(o,c);
return;
}
pushdown(o);
int mid=(l+r)>>1;
if(L<=mid)
update_seg(o<<1,L,R,c);
if(R>mid)
update_seg(o<<1|1,L,R,c);
pushup(o);
}
void update_point(int o,int pos,ll c)
{
int l=root.l,r=root.r;
if(r<pos || l>pos)
return;
if(l==pos && r==pos)
{
root.val=add(root.val,c);
return;
}
int mid=(l+r)>>1;
if(pos<=mid)
update_point(o<<1,pos,c);
if(pos>mid)
update_point(o<<1|1,pos,c);
pushup(o);
}
ll Query(int o,int L,int R)
{
int l=root.l,r=root.r;
if(r<L || l>R)
return 0;
if(L<=l && r<=R)
return root.val;
ll res=0;
pushdown(o);
int mid=(l+r)>>1;
if(L<=mid)
res=add(res,Query(o<<1,L,R));
if(R>mid)
res=add(res,Query(o<<1|1,L,R));
return res;
}
}Tmax,Tmin;
int n,m;
int c[MAXN],del[MAXN>>1];
ll fac[MAXN],invfac[MAXN];
inline ll Catalan(int x)
{
return mul(mul(fac[2*x],invfac[x]),invfac[x+1]);
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=2*n;++i)
c[i]=read();
for(int i=1;i<=n;++i)
del[i]=c[2*i]-c[2*i-1];
fac[1]=invfac[1]=1;
for(int i=2;i<=2*n;++i)
{
fac[i]=mul(fac[i-1],i);
invfac[i]=inv(fac[i]);
}
// for(int i=1;i<=10;++i)
// printf("%lld,",Catalan(i));
Tmax.BuildTree(1,1,2*n,c);
// cerr<<"1"<<endl;
Tmin.BuildTree(1,1,n,del);
while(m--)
{
int opt=read();
if(opt==0)
{
int L=read(),R=read(),val=read();
Tmax.update_seg(1,L,R,1ll*val);
if(L%2==0)
{
Tmin.update_point(1,L>>1,val);
Tmin.update_point(1,(R+1)>>1,-val);
}
}
else
{
int L=read(),R=read();
int Mid=(L+R)>>1;
ll maxans=sub(Tmax.Query(1,Mid+1,R),Tmax.Query(1,L,Mid));
printf("%lld ",maxans);
ll minans;
if(L&1)
{
minans=Tmin.Query(1,(L+1)>>1,R>>1);
}
else
{
int nl=L+1,nr=R-1;
if(nl>0 && nl<=2*n && nr>0 && nr<=2*n && nl<nr)
minans=Tmin.Query(1,(nl+1)>>1,nr>>1);
else
minans=0;
minans=mul(minans,-1);
minans=add(minans,Tmax.Query(1,R,R));
minans=sub(minans,Tmax.Query(1,L,L));
}
printf("%lld ",minans);
int len=(R-L+1)>>1;
printf("%lld\n",Catalan(len));
}
}
return 0;
}
T3
資料範圍 \(l,r,k \leq 1e18\),\(k \geq 2\).
接手T3時,因為上個題的sb除錯,只剩10分鐘了...
感覺這題可能要GG,但好在此題暴力十分好寫(絕對比T2暴力好寫幾個數量級) ,怒敲5分鐘暴力,毅然提交...
弱智暴力就沒啥必要貼程式碼了吧...