T1
大水题+模拟
直接二分$O(nlogn)$
然而有趣的是我在确认输入不会炸long long后
认为中间的乘法也不会炸
然后就从考完试到下午4点,一直在调,重打了两遍...
就差3个1LL...
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; inline void read(int &x) { x=0; int ff=1; char q=getchar(); while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); } while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); x*=ff; } const int N=100006; struct JI { int lx,ly,rx,ry; }ji[N]; bool cmp_x(JI a,JI b) { if(a.lx==b.lx) return a.ly<b.ly; return a.lx<b.lx; } bool cmp_y(JI a,JI b) { if(a.ly==b.ly) return a.lx<b.lx; return a.ly<b.ly; } int n; ll an,an2; int er1(int x,int y,int l) { int r=n,mid,ans=0; while(l<=r) { mid=(l+r)>>1; if(ji[mid].lx>x) r=mid-1; else if(ji[mid].lx<x) l=mid+1; else { if(ji[mid].ly<=y) ans=mid,l=mid+1; else r=mid-1; } } return ans; } int er2(int x,int y,int l) { int r=n,mid,ans=0; while(l<=r) { mid=(l+r)>>1; if(ji[mid].ly<y) l=mid+1; else if(ji[mid].ly>y) r=mid-1; else { if(ji[mid].lx<=x) ans=mid,l=mid+1; else r=mid-1; } } return ans; } void work() { rint i,j; int tt,q1,q2,q3,q4; sort(ji+1,ji+1+n,cmp_x); for(i=1;i<=n;++i) { tt=er1(ji[i].rx+1,ji[i].ry+1,i+1); for(j=tt;j>i;--j) { if( ji[j].lx!=ji[i].rx+1 || ji[j].ry<ji[i].ly-1 ) break; if(ji[j].ly>ji[i].ry) { if(ji[j].ly==ji[i].ry+1) ++an2; continue; } if(ji[j].ry<ji[i].ly) { if(ji[j].ry==ji[i].ly-1) ++an2; continue; } q1=min(ji[i].ly,ji[j].ly); q2=q1^ji[i].ly^ji[j].ly; q4=max(ji[i].ry,ji[j].ry); q3=q4^ji[i].ry^ji[j].ry; an+=1LL*(q3-q2)*2; if(q1<q2) ++an; if(q4>q3) ++an; } } sort(ji+1,ji+1+n,cmp_y); for(i=1;i<=n;++i) { tt=er2(ji[i].rx+1,ji[i].ry+1,i+1); for(j=tt;j>i;--j) { if( ji[j].ly!=ji[i].ry+1 || ji[j].rx<ji[i].lx-1 ) break; if(ji[j].lx>ji[i].rx) { if(ji[j].lx==ji[i].rx+1) ++an2; continue; } if(ji[j].rx<ji[i].lx) { if(ji[j].rx==ji[i].lx-1) ++an2; continue; } q1=min(ji[i].lx,ji[j].lx); q2=q1^ji[i].lx^ji[j].lx; q4=max(ji[i].rx,ji[j].rx); q3=q4^ji[i].rx^ji[j].rx; an+=1LL*(q3-q2)*2; if(q1<q2) ++an; if(q4>q3) ++an; } } } int main(){ //freopen("T1.in","r",stdin); //freopen("ljh1.in","r",stdin); rint i; read(n); for(i=1;i<=n;++i) { read(ji[i].lx); read(ji[i].ly); read(ji[i].rx); read(ji[i].ry); an=an+1LL*(ji[i].ry-ji[i].ly)*(ji[i].rx-ji[i].lx)*2; } work(); cout<<an+an2/2; }
T2
这个题要在每个节点维护一个map记录子树里每种颜色第一次出现的时间戳
一颗动态开点的以时间戳为下标的线段树来记录每一段时间的颜色种数
我们先来考虑如果一次操作对一个节点有贡献,要满足两个条件
1.这次操作的时间戳小于等于这个节点的k值所能到的最大时间戳 (dfs序,在用随便一个可持久化数据结构可以求出区间k大值)
2.这次操作的颜色必须是第一次出现
对于第一个条件,我们先求出dfs序,将每一个操作铺成一个线段,线段上每一个点都是一个操作
然后建一颗可持久化01trie主席树什么的,求出$limit_x$ 代表x这个子树放小球能放的最大时间戳
那么每一个节点的ans就是在这个节点的线段树$[1,limit_x]$的不同颜色种数
那么递归处理问题
假设已经将x的儿子都处理完了
将信息合并上来就行了
启发式合并就行了...
#pragma GCC optimize("O2") #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <vector> #include <map> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; inline void read(int &x) { x=0; int ff=1; char q=getchar(); while(q<'0'||q>'9') { if(q=='-') ff=-1; q=getchar(); } while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); x*=ff; } const int N=100006; int first[N*2],nt[N*2],ver[N*2],e; void addbian(int u,int v) { ver[e]=v; nt[e]=first[u]; first[u]=e++; } int n,m,Q; int maxk[N]; int limit[N],an[N]; int qi[N],ho[N],fa[N],nw,tim[N]; int pos[N]; map<int,int> mp[N]; vector<int> ji[N]; struct seg { int sz; seg *ch[2]; seg(){sz=0;ch[0]=ch[1]=NULL;} inline void pushup() { sz=0; if(ch[0]!=NULL) sz+=ch[0]->sz; if(ch[1]!=NULL) sz+=ch[1]->sz; } }*rt[N]; void addseg(int pos,int vv,int l,int r,seg *&x) { if(x==NULL) x=new seg(); if(l==r) { x->sz+=vv; return ; } int mid=(l+r)>>1; if(pos<=mid) addseg(pos,vv,l,mid,x->ch[0]); else addseg(pos,vv,mid+1,r,x->ch[1]); x->pushup(); } void qqseg(int L,int R,int &an,int l,int r,seg *&x) { if(L>R) return ; if(x==NULL) return ; if(L<=l&&r<=R) { an+=x->sz; return ; } int mid=(l+r)>>1; if(L<=mid) qqseg(L,R,an,l,mid,x->ch[0]); if(mid<R) qqseg(L,R,an,mid+1,r,x->ch[1]); } struct trie { int sum; trie *ch[2]; trie(){ch[0]=ch[1]=NULL;sum=0;} }*root[N]; void addtrie(int order) { root[order]=new trie(); root[order]->sum=root[order-1]->sum+1; trie *pr=root[order-1],*now=root[order]; int tt,i; for(i=30;i>=0;--i) { tt=((tim[order]&(1<<i))>>i); now->ch[tt]=new trie(); now->ch[tt]->sum=pr->ch[tt]->sum+1; now->ch[tt^1]=pr->ch[tt^1]; now=now->ch[tt]; pr=pr->ch[tt]; } } int qqtrie(int l,int r,int k) { if(l>r) return 0; trie *pr=root[l-1],*now=root[r]; int i,ans=0,t0,t1,tt; for(i=30;i>=0;--i) { t0=now->ch[0]->sum-pr->ch[0]->sum; t1=now->ch[1]->sum-pr->ch[1]->sum; if(!t0||!t1) { if(!t0&&!t1) return ans; if(t0) tt=0; else ans|=(1<<i),tt=1; } else { if(t0>=k) tt=0; else ans|=(1<<i),tt=1,k-=t0; } now=now->ch[tt]; pr=pr->ch[tt]; } return ans; } void dfs1(int x) { qi[x]=nw; int i,sz=ji[x].size(); for(i=0;i<sz;++i) tim[++nw]=ji[x][i]; for(i=first[x];i!=-1;i=nt[i]) { if(ver[i]==fa[x]) continue; fa[ver[i]]=x; dfs1(ver[i]); } ho[x]=nw; } void get_limit() { rint i; dfs1(1); root[0]=new trie(); root[0]->ch[0]=root[0]->ch[1]=root[0]; root[0]->sum=0; for(i=1;i<=nw;++i) addtrie(i); for(i=1;i<=n;++i) { if(maxk[i]==0) limit[i]=0; else limit[i]=qqtrie(qi[i]+1,ho[i],maxk[i]); } } map<int,int> :: iterator it; void mer(int x,int y) { int clo; for(it=mp[pos[x]].begin();it!=mp[pos[x]].end();++it) { clo=it->first; if(!mp[pos[y]].count(clo)) mp[pos[y]][clo]=it->second,addseg(it->second,1,1,m,rt[pos[y]]); else if(mp[pos[y]][clo]>it->second) addseg(mp[pos[y]][clo],-1,1,m,rt[pos[y]]),mp[pos[y]][clo]=it->second,addseg(it->second,1,1,m,rt[pos[y]]); } } void dfs(int x) { int i,t1,t2; for(i=first[x];i!=-1;i=nt[i]) { if(ver[i]==fa[x]) continue; dfs(ver[i]); t1=mp[pos[x]].size(); t2=mp[pos[ver[i]]].size(); if(t1>t2) mer(ver[i],x); else mer(x,ver[i]),swap(pos[x],pos[ver[i]]); } qqseg(1,limit[x],an[x],1,m,rt[pos[x]]); } int main(){ //freopen("T2.in","r",stdin); //freopen("T2.out","w",stdout); rint i,j; mem(first,-1); read(n); int tin1,tin2; for(i=1;i<n;++i) { read(tin1); read(tin2); addbian(tin1,tin2); addbian(tin2,tin1); } for(i=1;i<=n;++i) read(maxk[i]); read(m); for(i=1;i<=m;++i) { read(tin1); read(tin2); ji[tin1].push_back(i); if(!mp[tin1].count(tin2)) mp[tin1][tin2]=i,addseg(i,1,1,m,rt[tin1]); } get_limit(); for(i=1;i<=n;++i) pos[i]=i; dfs(1); read(Q); for(i=1;i<=Q;++i) { read(tin1); printf("%d ",an[tin1]); } }
T3
首先将问题转化成求 每一天$m^n$种情况下的劳累度总和
$$ans=frac{C_{k}^{j}*(i-1)^{k-j}*m^{n-k}*w_i*(n-k+1)}{m^{n}}$$
i是枚举这一天的最大值,j是枚举最大值的个数
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int N=506; const int mod=1000000007; ll qpow(ll a,int ci) { ll ans=1; while(ci) { if(ci&1) ans=ans*a%mod; a=a*a%mod; ci>>=1; } return ans%mod; } int n,m,K; int w[N]; ll C[N][N],mi[N][N]; int main(){ //freopen("T3.in","r",stdin); rint i,j; read(n); read(m); read(K); for(i=1;i<=m;++i) read(w[i]); if(n<K) { puts("0"); return 0; } for(i=0;i<=K;++i) { C[i][0]=C[i][i]=1; for(j=1;j<i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } //for(i=0;i<=m;++i) // mi[0][i]=1; mi[0][0]=1; for(i=1;i<=m;++i) { mi[i][0]=1; for(j=1;j<=n;++j) mi[i][j]=mi[i][j-1]*i%mod; } ll an=0; for(i=1;i<=m;++i) for(j=1;j<=K;++j) an=(an+C[K][j]*mi[i-1][K-j]%mod*w[i]%mod)%mod; an=an*mi[m][n-K]%mod*(n-K+1)%mod; an=an*qpow(mi[m][n],mod-2)%mod; cout<<an; }
这一天的题好神啊...