~~bzoj早就老了~~如今最受欢迎的OJ固然是你谷了,一些比较经典的题目仍是很不错的。ios
LINK:想了好几个月的dp 翻硬币 我想到一个状态 f[i][j]表示翻了i次且当前局面为j的方案数最后输出f[n][目标方案便可]。c++
可是这里面存在着一些问题 好像这个转移都是一个组合数级别的因此不可能转移出来的 。忽然有一个想法设f[i][j]表示通过i次翻转有j个位置是合法的方案数这样就能够转移了。数组
n^2递推组合数便可。ide
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll 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; } const ll MAXN=110; ll n,m,k,cnt; ll c[MAXN][MAXN]; ll f[MAXN][MAXN];//f[i][j]表示前i次翻转有j个位置合法的方案数。 char a[MAXN],b[MAXN]; inline void get_combination() { for(ll i=0;i<=n;++i)c[i][0]=1; for(ll i=1;i<=n;++i)c[i][1]=i; for(ll i=1;i<=n;++i) for(ll j=1;j<=i;++j) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; } signed main() { //freopen("1.in","r",stdin); n=read();k=read();m=read(); scanf("%s",a+1); scanf("%s",b+1); for(ll i=1;i<=n;++i) if(a[i]==b[i])++cnt; get_combination(); f[0][cnt]=1; for(ll i=1;i<=k;++i) { for(ll l=0;l<=n;++l)//枚举上一次状态 { if(!f[i-1][l])continue; for(ll j=0;j<=n;++j)//枚举当前状态 { if(abs(l-j)>m)continue; ll s1=abs(l-j); if((m-s1)&1)continue; ll s2=(m-s1)>>1; ll rest=n-l; if(j>=l) { if(s1+s2<=rest)f[i][j]=(f[i][j]+((f[i-1][l]*c[rest][s1+s2]%mod)*c[l][s2])%mod)%mod; } else if(s1+s2<=l)f[i][j]=(f[i][j]+((f[i-1][l]*c[rest][s2]%mod)*c[l][s1+s2])%mod)%mod; } } } printf("%lld\n",f[k][n]); return 0; }
LINK:系统设计想了2h都没思路的题目XR-round系列 才知道有这种题目的存在,好难想啊。学习
当时比赛的时候都没能想出来 一棵有根树每次指定一个节点开始沿着m这个序列向下跑问中止时到达哪一个节点了?显然的nQ暴力,除此以外也就没有什么了。优化
可是呢$n,Q<=200000$这显然是不能过的,考虑一下正解?离线?由于还带修改因此基本上须要在线搞。ui
观察 修改的是什么m这个序列 咱们从题目自己下手 修改这个序列是为了阻止咱们暴力的干某件事情因此进行了修改。其实就算不修改咱们离线以后尽管每一个点都便利了一次可是最后的复杂度仍免不了是MQ的照样歇菜。spa
如何对于一棵树的某一条链和这个序列的l r进行快速匹配呢?咱们发现了咱们先对树的每一层进行标号 而后设根到某个节点x的路径上的字符串为sx那么发现了什么这个字符串最多有O(n)。设计
因此对于一次询问咱们快速提取出l到r这个区间内的字符串再加上询问的从x出发的那个节点sx且若是存在咱们就获得了目标节点的字符串信息了直接查找。3d
查找的话仍是字符串hash比较好一点这样若是保证存在咱们直接在主席树上找是否有这个值出现便可。可是若是不存在怎么办?这是一个比较严重的问题好像很难解决的样子,不过好像也是能够的考虑到答案必然是某个节点咱们不妨二分一下深度 答案之上的节点咱们必然也能够找到 因此显然二分是成立的。复杂度$Q(logn)^2$可是注意到一个问题带修改的m序列hash值也在变暴力一点直接线段树维护hash值结束..
可是太暴力了,写两棵树我不是很想写,看了题解 发现果真和题解差的好远,题解直接用线段树维护了hash值,而后查找直接线段树上二分省去了我那个二分的log而后断定的话直接hash表中进行断定。
真的是简洁,那么这道题就这样作便可。可是通过不懈的对拍发现了天然溢出hash发生了冲突 在这里咱们很难使用挂链法由于咱们就是想要尽量的没有冲突。
采用双hash双map...(固然可能会很慢.. 仍是被卡了 单模数hashwa了?我忍了 双模数hash也wa了?我也忍了..不写了回来请教一下举办这场比赛的人再说.
因而我开大了进制数 书上说 131 13331 这两个进制数冲突率极低 可是这两个我都试了一直wa 改模数也是wa 开大到1000000007 发现就不wa了。很开心。
可是T了... 常数实在是太大了 线段树上二分 + map 两个常数极大的东西 开o2也无论用。
终于被我找到了方法 手动hash 关掉map .. 由于树状数组上倍增对于这道题来讲实在是太难写了。我真不会quq
因此 开了拉链法 注:1000003 是一个质数 而1000007 不是质数。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define ull unsigned long long #define un unsigned #define P 1000000007ull #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define ans(p) t[p].ans using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=500010,Mod=1000003; int n,m,Q,root,rt,id,len; ull f[MAXN],p[MAXN],ans,c,ver[Mod],ver1[Mod]; int a[MAXN],lin[Mod],nex[Mod]; vector<int>g[MAXN]; inline void add(int x,int y,ull z) { ver[++len]=y; ver1[len]=z; nex[len]=lin[x]; lin[x]=len; } inline void insert(ull x,int y) { int w=x%Mod; add(w,y,x); } inline int find(ull x) { int w=x%Mod; for(int i=lin[w];i;i=nex[i]) if(ver1[i]==x)return ver[i]; return 0; } struct wy { int l,r; ull sum; int ans; }t[MAXN<<2]; inline void dfs(int x) { int cnt=0; sort(g[x].begin(),g[x].end()); for(un int i=0;i<g[x].size();++i) { int tn=g[x][i]; ++cnt; f[tn]=f[x]*P+cnt; insert(f[tn],tn); dfs(tn); } } inline void pushup(int w){sum(w)=sum(l(w))*p[ans(r(w))]+sum(r(w));} inline void build(int &p,int l,int r) { if(!p)p=++id; t[p].ans=(r-l+1); if(l==r){sum(p)=a[l];return;} int mid=(l+r)>>1; build(l(p),l,mid); build(r(p),mid+1,r); pushup(p); } inline int ask(int s,int l,int r,int x) { if(l==r) { c=ans*p[ans(s)]+sum(s); if(find(c)) { ans=c; return r; } return l-1; } int mid=(l+r)>>1; if(l>=x) { c=ans*p[ans(s)]+sum(s); if(find(c)) { ans=c; return r; } c=ans*p[ans(l(s))]+sum(l(s)); if(find(c)) { ans=c; return ask(r(s),mid+1,r,x); } return ask(l(s),l,mid,x); } if(x>mid)return ask(r(s),mid+1,r,x); int w=ask(l(s),l,mid,x); if(w==mid) { c=ans*p[ans(r(s))]+sum(r(s)); if(find(c)) { ans=c; return r; } return ask(r(s),mid+1,r,x); } return w; } inline void query(int s,int l,int r,int L,int R) { if(L>R)return; if(L<=l&&R>=r) { ans=ans*p[ans(s)]+sum(s); //cout<<s<<endl; return; } int mid=(l+r)>>1; if(L<=mid)query(l(s),l,mid,L,R); if(R>mid)query(r(s),mid+1,r,L,R); return; } inline void modify(int p,int l,int r,int x,int w) { if(l==r){sum(p)=w;return;} int mid=(l+r)>>1; if(x<=mid)modify(l(p),l,mid,x,w); if(x>mid)modify(r(p),mid+1,r,x,w); pushup(p); } int main() { //freopen("1.in","r",stdin); n=read();m=read();Q=read();p[0]=1; for(int i=1;i<=n;++i) { int x=read(); p[i]=p[i-1]*P; if(!x)root=i; else g[x].push_back(i); } dfs(root);insert(0,root); //for(int i=1;i<=n;++i)cout<<f[i]<<endl; for(int i=1;i<=m;++i)a[i]=read(); build(rt,1,m); //ans=0;query(rt,1,n,1,2); //cout<<ans<<endl; for(int i=1;i<=Q;++i) { int op,l,r,x; op=read(); if(op==1) { x=read();l=read();r=read(); ans=f[x]; int w=ask(rt,1,m,l); w=min(w,r); ans=f[x]; query(rt,1,m,l,w); printf("%d\n",find(ans)); } else { int y; x=read();y=read(); modify(rt,1,m,x,y); } } return 0; }
很开心。
LINK:古代猪文 这题好啊 SDOI 2010 三道猪的神题..果真强大。一句话题意 求 $G^{\sum_{d|n}{C(d,n)}}mod999911659$
其中C是组合数 设p=999911659 当G是p的倍数显然为0 考虑不是p的倍数的时候咱们显然能够发现p是个质数 那么直接欧拉降幂Y,
降幂以后至关于指数%上p-1 如今求一个组合数%p-1便可。这个d咱们能够根号n枚举 在能够承受的范围内。关键是如何求一个组合数模p-1.
p-1显然不是一个质数 不能卢卡斯定理 可是 咱们 能够发现一个问题 p-1=2*3*166651943 暴力枚举166651943 发现能整除的有4679 35617 再次暴力枚举4679 和35617 发现这两个都是质数
也就是说p-1分解成了4个质数且这四个质数的次数都为1 那么 咱们显然能够利用组合数模这个质数的答案而后,发现了什么其实咱们答案%p-1很难搞可是答案%这四个质数答案很好搞。
也就是有了4个同余方程 且合并完以后恰好是咱们的p-1 那么同余方程一解 快速幂便可。
码了一个h 写写停停仍是对数论的一些定理不是太熟悉..
#//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned using namespace std; char *ft,*fs,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll p=999911659; ll G,n,ans[5],an,xx,yy; ll jc[5][400000],inv[5][400000]; ll y[5]={0,2,3,4679,35617}; inline ll gcd(ll a,ll b) { if(!b)return a; return gcd(b,a%b); } inline ll Lucas(ll a,ll b,ll mod,ll w) { if(a<b)return 0; if(a==b)return 1; if(b==0)return 1; if(a<mod)return ((jc[w][a]*inv[w][b])%mod*inv[w][a-b])%mod; return (Lucas(a%mod,b%mod,mod,w)*Lucas(a/mod,b/mod,mod,w))%mod; } inline ll ksm(ll b,ll pp,ll mod) { ll ans=1; while(pp) { if(pp&1)ans=ans*b%mod; b=b*b%mod; pp=pp>>1; } return ans; } inline void get_ans(ll x)//求C(n,x) { for(ll i=1;i<=4;++i) ans[i]+=Lucas(n,x,y[i],i); } inline void exgcd(ll a,ll b) { if(!b){xx=1,yy=0;return;} exgcd(b,a%b); ll z=xx;xx=yy;yy=z-a/b*yy; } inline void merge()//同余方程求答案 { for(ll i=1;i<=4;++i)//显然通解是x=Mi*Ti*ai { ll M=(p-1)/y[i];//求解 Ti Mi*Ti=ai(mod y[i]) exgcd(M,y[i]); xx=(xx%y[i]+y[i])%y[i]; an=(an+(M*xx)%(p-1)*ans[i])%(p-1); } } inline void solve()//求G的次数ans { for(ll i=1;i*i<=n;++i) if(n%i==0) { get_ans(i); if(i!=n/i)get_ans(n/i); } merge(); return; } signed main() { //freopen("1.in","r",stdin); n=read();G=read(); if(gcd(G,p)==p){puts("0");return 0;} for(ll i=1;i<=4;++i) { jc[i][0]=1; for(ll j=1;j<y[i];++j) jc[i][j]=jc[i][j-1]*j%y[i]; inv[i][y[i]-1]=ksm(jc[i][y[i]-1],y[i]-2,y[i]); for(ll j=y[i]-2;j>=1;--j) inv[i][j]=inv[i][j+1]*(j+1)%y[i]; } solve(); printf("%d\n",ksm(G,an,p)); return 0; }
LINK:最短路的变形问题 其实就是随机删掉一条边在最差的状况下1 到 n的最短路 显然咱们任意求出一条最短路 而后若是删掉的不是最短路上的边那么答案不变。
考虑 枚举删掉最短路的边 而后再跑最短路 每次取max便可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<cctype> #include<algorithm> #include<cstdlib> #include<queue> #include<stack> #include<set> #include<bitset> #include<vector> #include<map> #include<deque> #include<utility> #define INF 1000000000 #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define mp make_pair #define S second using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010,maxn=1000010; int n,m,len=1,top,mark,ans; int pre[MAXN],s[MAXN],dis[MAXN],vis[MAXN]; int lin[MAXN],ver[maxn<<1],nex[maxn<<1],e[maxn<<1]; priority_queue<pair<int,int> > q; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void dij() { for(int i=1;i<=n;++i)vis[i]=0,dis[i]=INF; dis[1]=0;q.push(mp(0,1)); while(q.size()) { int x=q.top().S;q.pop(); if(vis[x])continue; vis[x]=1; for(int i=lin[x];i;i=nex[i]) { if(i==mark||((i^1)==mark))continue; int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; pre[tn]=i; q.push(mp(-dis[tn],tn)); } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } dij(); int w=n; while(w!=1) { s[++top]=pre[w]; w=ver[pre[w]^1]; } for(int i=1;i<=top;++i) { mark=s[i]; dij(); ans=max(ans,dis[n]); } printf("%d\n",ans); return 0; }
可是复杂度不够优秀 n^2logn 我以为不太好...考虑更优秀的作法。
仍是上一道高难度的题目比较有说服力:LINK:道路堵塞 每次都去掉一条边 而后询问 最短路是多长。
n<100000 m<200000 这个范围 咱们标记最短路若是询问不是最短路上的边咱们就不跑是的话咱们就再跑一边这样的复杂度显然仍是上述的复杂度。。
考虑优化..本人不会先咕了。
我确定 咕不超过两个月这个问题激怒了 我 学习了一种比较假的作法(毕竟spfa已经死了 在升级以前也不可能复活了。
具体 作法是这样的 题目中已经给出了最短路 首先暴力显然是 暴力删边 而后暴力跑。复杂度太高。
考虑动态的spfa 什么也就是咱们只spfa一次 求出全部的 答案。虽然复杂度最坏nm 可是 显然 在一些状况下出题人仍是会让spfa 复生的。
首先把全部的边都ban掉 求出 $g_i$ 表示最短路上的第i个点到终点的最短路。 先从1跑一边spfa但 最短路上的边都不能用。
而后 一旦跑到最短路上的某一点 那么 就多是答案 $dis_i+g_i$ 这样的话就成为了答案的候选项 加入堆中。
考虑 不断启用 最短路上边 可是同时也不能用当前的最短路 也就是当前的最短路以前的点都是不合法的因此pop 。
总复杂度 1次spfa的操做 nt 吧 /cy
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<int,int> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=200010; int n,m,len,L,t,h; int pos[MAXN],dis[MAXN],q[MAXN<<4]; int w[MAXN],vis[MAXN],g[MAXN],id[MAXN],mark[MAXN]; int lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN]; priority_queue<pii> s; inline void add(int x,int y,int z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline void spfa(int xx) { t=h=0; q[++t]=xx;vis[xx]=1; while(h++<t) { int x=q[h];vis[x]=0; for(int i=lin[x];i;i=nex[i]) { if(mark[i])continue; int tn=ver[i]; if(dis[tn]>dis[x]+e[i]) { dis[tn]=dis[x]+e[i]; if(id[tn])s.push(mk(-(dis[tn]+g[id[tn]]),id[tn])); else if(!vis[tn])q[++t]=vis[tn]=tn; } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();L=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); add(x,y,read()); } for(int i=1;i<=L;++i) { w[i]=read(); mark[w[i]]=1; pos[i+1]=ver[w[i]]; id[ver[w[i]]]=i+1; } memset(dis,0x3f,sizeof(dis)); id[1]=pos[1]=1; for(int i=L;i>=1;--i)g[i]=g[i+1]+e[w[i]]; dis[1]=0;spfa(1); for(int i=1;i<=L;++i) { while(s.size()&&s.top().second<=i)s.pop(); if(!s.size())puts("-1"); else printf("%d\n",-s.top().first); dis[pos[i+1]]=dis[pos[i]]+e[w[i]]; spfa(pos[i+1]); } return 0; }
清醒了一下思考了 一下 个人拓扑的思路是错误的 一条边随机的不能走,而不是一个最优解被舍弃,这样作法是不正确的 由于存在剩下的解不是最差解中的最优解。
考虑dp f[i][j]表示到了第i个点此时通过k次状况造成的最优解 仔细思考咱们不知道后续的状态可能这个状态没法达到最差的状态,因此还须要更改。
不难发现 想要转移须要后续状态的加持 再设一个更清晰的状态 f[i][j] 表示到达第i个点此时还有j次不可控的状况未发生且到达终点的最优解这个显然是min max 转移我想。
显然有终点状态 f[n][0]=0;在一张DAG上直接搜便可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 20000000000000000ll #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define d(i) t[i].d #define x(i) t[i].x #define db double using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll MAXN=150010; ll n,m,k,t,h,len; ll lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN]; ll f[MAXN][15],q[MAXN],tmp[MAXN],ru[MAXN]; inline void add(ll x,ll y,ll z) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; e[len]=z; } inline int dfs(int x,int s) { if(f[x][s]>=0)return f[x][s]; ll minn=INF,maxx=0; if(s>=1) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; dfs(tn,s-1); minn=min(minn,f[tn][s-1]+e[i]); } } for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; dfs(tn,s); maxx=max(maxx,f[tn][s]+e[i]); } f[x][s]=min(minn,maxx); return f[x][s]; } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); memset(f,0xcf,sizeof(f)); for(ll i=1;i<=m;++i) { ll x,y,z; x=read();y=read();z=read(); add(x,y,z); } f[n][0]=0; dfs(1,k); printf("%lld\n",f[1][k]); return 0; }
注意转移 max 套min 便可。
对于答案显然是二分 可是并很差写 由于check的缘由 只有一堆信号塔 以及最左端和最右端 将其都话点最后判断 最左端和最右端是否相连,这个显然是很差搞得仍是存在 最左端出现多个表明位置。
不妨维护某个集合的最大向左延伸 和最大向右延伸便可。而后n^2并查集 这个排序可能作不到O(n)可能存在两个点的横坐标相同因此O(n)不太好作因此就采用暴力枚举了。
因此此题 想仍是很好想的作法须要斟酌一番。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 20000000000000000ll #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010; int n,m; struct wy { int x,y; }t[MAXN]; int f[MAXN]; db L[MAXN],R[MAXN]; inline db dis(int x,int y) { return sqrt(1.0*(t[x].x-t[y].x)*(t[x].x-t[y].x)+(ll)(t[x].y-t[y].y)*(ll)(t[x].y-t[y].y)); } inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);} inline int check(db x) { for(int i=1;i<=m;++i) { L[i]=t[i].x-x; R[i]=t[i].x+x; f[i]=i; //求最长向左延伸和最长向右延伸 } for(int i=1;i<=m;++i) for(int j=i+1;j<=m;++j) { db w=dis(i,j); if(w<=x+x) { int xx=getfather(i); int yy=getfather(j); if(xx==yy)continue; f[xx]=yy; L[yy]=min(L[yy],L[xx]); R[yy]=max(R[yy],R[xx]); } } for(int i=1;i<=m;++i) { int xx=getfather(i); if(L[xx]<=0&&R[xx]>=n)return 1; } return 0; } int main() { freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); t[i]=(wy){x,y}; } db l=0,r=n; while(l+EPS<r) { db mid=(l+r)/2; if(check(mid))r=mid; else l=mid; } printf("%.2lf\n",l); return 0; }
题目是很简单 操做 将一个区间内全部比Hi小的数改成Hi 区间很大 考虑线段树 但是离散以后貌似也没有什么更好的作法,考虑离散化以后暴力的搞最后统一下传懒标记可是存在一个问题咱们把区间缩掉了,统计答案的时候咱们不知道当前某两个点之间的的值究竟是0仍是存在对答案是有贡献的这显然很难维护这个东西 因此考虑动态开点。
可是我指望的作法是标记永久花 区间这么大可是一些区间是没必要要的考虑 动态开点 但是这样加入懒标记以后不能pushdown 由于一旦pushdown就会下传好多的节点 新建好多节点致使时间和空间双双爆炸。
考虑标记永久化 最后 便利一下整颗树来统计答案。固然这个我也不太熟悉 本身随便写一个好了。
再三斟酌仍是本身写(找不到之前的标记永久化代码了头疼...乌拉随便写的居然过了 虽然数组开小了答案 算的有点锅。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define l(x) t[x].l #define add(x) t[x].add #define r(x) t[x].r using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=40010; int n,maxx=1000000000; int root,id; ll ans; struct wy { int l,r; int add; }t[MAXN*30<<1]; inline void change(int &p,int l,int r,int L,int R,int x) { if(!p)p=++id; if(L==l&&R==r) { add(p)=max(add(p),x); return; } int mid=(l+r)>>1; if(L>mid)change(r(p),mid+1,r,L,R,x); else { if(R<=mid)change(l(p),l,mid,L,R,x); else change(l(p),l,mid,L,mid,x),change(r(p),mid+1,r,mid+1,R,x); } } inline void ask(int p,int l,int r,ll mx)//mx表示当前区间的最大值 { mx=mx>add(p)?mx:add(p); if(l==r){ans+=mx;return;} if(!p) { ans+=(r-l+1)*mx; return; } int mid=(l+r)>>1; ask(l(p),l,mid,mx); ask(r(p),mid+1,r,mx); return; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { int x,y,z; x=read();y=read()-1;z=read(); change(root,1,maxx,x,y,z); } ask(root,1,maxx,0); printf("%lld\n",ans); return 0; }
对于这个代码的复杂度咱们显然能够看出 区间修改时复杂度为logn 又把区间分红logn段 总共有nlogn段最后统计一遍树只会统计到有值得点因此复杂度仍为nlogn.
LINK:WQS二分模板题 早就想写了。
刚好有need 条边的生成树而且使这颗生成树的权值和最小 因此考虑 dp 这玩意最小生成树 不太行了就咱们从dp的方面想办法。
这里咱们先无论答案怎么求设 fix 表示在前i条边中强制选择了x条边的的最小价值。 这 画一张图 发现这造成了一个凸包 考虑如何求出最优解 一个比较靠谱的作法是 用一条线来切这个凸包 关于凸包上
本人仍是不会 因此 仍是咕了。
LINK:同类分布 一道神仙 数位dp 好题啊 我根本就不会作QuQ
考虑暴力 直接爆搜 考虑dp 咱们发现模数很烦 由于咱们只有分配数字到最后才能知道模数是多少 不妨咱们直接暴力一点枚举模数。
而后对于 就能够直接设计状态 $f_{i,j,k}$ 表示到了第i位 此时 余数是j 和位 k的方案数。
这样就能够dp下去了 复杂度 大概为 1e8左右 不过值得注意的是 mark标记的维护。即最高位的限制。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=9*20+1,maxn=20; ll f[maxn][MAXN][MAXN];//f[i][j][k]表示到了第i位 此时余数是j 和为k的方案数 ll n,m,ans,cnt,top,mod; ll b[maxn]; inline ll dfs(ll pos,ll res,ll sum,ll mark)//mark 表示是否 有最高位的限制 { if(!mark&&f[pos][res][sum]!=-1)return f[pos][res][sum]; if(!pos) { if(!res&&sum==mod)f[pos][res][sum]=1; else f[pos][res][sum]=0; return f[pos][res][sum]; } ll ans=0,maxx=mark?b[pos]:9; for(int i=0;i<=maxx;++i) { if(i==maxx)ans+=dfs(pos-1,(res*10+i)%mod,sum+i,mark&1); else ans+=dfs(pos-1,(res*10+i)%mod,sum+i,0); } if(!mark)f[pos][res][sum]=ans; return ans; } inline ll slove(ll x) { top=0;ll cnt=0; while(x) { b[++top]=x%10; x=x/10; } for(int i=1;i<=top*9;++i) { memset(f,-1,sizeof(f)); mod=i; cnt=cnt+dfs(top,0,0,1); } return cnt; } signed main() { //freopen("1.in","r",stdin); m=read();n=read(); ans=slove(n); cnt=slove(m-1); printf("%lld\n",ans-cnt); return 0; }
LINK:末日的传说 想了几个月终于以为本身能够写这道题了。 ~~这多是我咕的最久的题目了~~
早就有了一个初步的想法了 可是后续的细节最近才想出来 我是作了旅行的增强版才对字典序理解的更加深入 固然 旅行那道题本身思考出来以后深入理解了什么叫作字典序。
当前能取最优就不要让当前较优后面更优 换句话说 当前只须要取到最优的决策便可 和后面的决策无关。
因此 考虑构造一个序列字典序最小且逆序对数有m个 咱们能够一位一位考虑这个作法 第一位能取1么 这得看后面的数字组成的逆序对数可否大于m了。
若是大于m的话当前显然 能够取到1 不行的话那么1必然要放到当前这个位置以后考虑接下来放的数字必然大于1 那么此时必然造成了一个逆序对 --m便可 一直寻找到这样的数字。不断作下去便可。
能够发现复杂度是n^2的固然过不了这道题 考虑加快寻找这个过程 显然主席树上二分便可 nlogn 不过代码复杂度太高 考虑树状数组+二分 因此以n(logn)^2的复杂度便可经过本题。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=50010; int n,m; int vis[MAXN]; int sum[MAXN],ans[MAXN]; int c[MAXN]; inline void add(int x,int y) { while(x<=n) { c[x]+=y; x+=x&(-x); } } inline int ask(int x) { int cnt=0; while(x) { cnt+=c[x]; x-=x&(-x); } return cnt; } inline int calc(int x) { int l=1,r=n; while(l+1<r) { int mid=(l+r)>>1; if(ask(mid)>=x)r=mid; else l=mid; } if(ask(l)>=x)return l; return r; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)sum[i]=(ll)i*(i-1)/2,add(i,1); for(int i=1;i<=n;++i) { int res=n-i;//除了当前这个数字以后还有多少位 int cnt=1,ww; ww=max(0,m-sum[res]); cnt+=ww; int w=calc(cnt); ans[i]=w; add(w,-1); m=m-ww; } for(int i=1;i<=n;++i)printf("%d ",ans[i]); return 0; }
固然 上述作法还不够优秀 显然还有更优的作法 仍是考虑上述的过程 1放不到这个位置以后咱们仍是想让当前数字取到最优 那么想要取到最优那么m就应该尽可能小 因此1放到最后一个位置m减少的幅度会很大 因此根据贪心的来选直接把1放到最后考虑下一个数字便可。
思考一下贪心的原则 能够发现这样是正确的 固然也有一个比较严谨的证实 能够自行脑补。复杂度O(n)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define pii pair<ll,ll> #define mk make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=50010; int n,m,st,en; int vis[MAXN]; int sum[MAXN],ans[MAXN]; int main() { //freopen("1.in","r",stdin); n=read();m=read();en=n+1; for(int i=1;i<=n;++i)sum[i]=(ll)i*(i-1)/2; for(int i=1;i<=n;++i) { int res=n-i;//除了当前这个数字以后还有多少位 if(sum[res]>=m)ans[++st]=i; else m-=res,ans[--en]=i; } for(int i=1;i<=n;++i)printf("%d ",ans[i]); return 0; }
LINK:区间第k小
1 直接sort 2 归并树 3 主席树。 第一种 nmlogn 无脑的东西 无聊 第二种 利用归并排序+二分+lower_bound无聊的方法 mlogn*logn*logn 这种作法虽然较劣 可是仍是 归并树的思想仍是要学习的。
3 主席树 比较经典的作法再也不赘述。代码是 主席树 可是归并树这个东西真的不多见。可能会用到的东西。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,m,id,minn=INF,maxx=-INF; int a[MAXN],root[MAXN]; struct wy { int l,r; int sum; }t[MAXN*30<<1]; inline void insert(int &p,int last,int l,int r,int x) { p=++id;t[p]=t[last]; if(l==r){++sum(p);return;} int mid=(l+r)>>1; if(x<=mid)insert(l(p),l(last),l,mid,x); else insert(r(p),r(last),mid+1,r,x); sum(p)=sum(l(p))+sum(r(p)); } inline int ask(int p,int last,int l,int r,int k) { if(l==r)return l; int mid=(l+r)>>1; int v=sum(l(p))-sum(l(last)); if(v>=k)return ask(l(p),l(last),l,mid,k); return ask(r(p),r(last),mid+1,r,k-v); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i) { a[i]=read(); minn=min(minn,a[i]); maxx=max(maxx,a[i]); } for(int i=1;i<=n;++i)insert(root[i],root[i-1],minn,maxx,a[i]); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); printf("%d\n",ask(root[y],root[x-1],minn,maxx,z)); } return 0; }
LINK:应该是二分的题目 想了一下以为挺有意思的 跟之前见过的模型差很少 总结一下。
一个序列中 求出一段连续子序列使其 平均数最大 ,有点脑残的问题 显然其中最大的那个数独自成为一个子序列就会使 答案最优 考虑 一个限制长度须要大于k 这就有一点点复杂了...
显然 遇到这种问题不防把式子写出来 $max{\frac{\sum_{a_i}}{w}}$ 考虑如何求出答案 显然是分数规划问题 不妨二分一下答案。考虑check。
在答案是mid时可能不存在这样的序列!但此时考虑若是存在最优解ans ans的长度必定是>=k的 因此 显然必然存在一个长度大于k的序列其累加和大于0 因此对于每个右端点寻找到一个最小的左端点便可。这样显然能够check答案。因此就解决了问题。我找到了题目的连接!
LINK:寻找段落 code:
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n; int S,T; int a[MAXN]; int minn=INF,maxx=-INF; db b[MAXN]; int q[MAXN],h,t; inline int check(db x) { db v=-INF; h=1;t=0; for(int i=1;i<S;++i)b[i]=a[i]-x+b[i-1]; q[++t]=0; for(int i=S;i<=n;++i) { b[i]=a[i]-x+b[i-1]; while(h<=t&&q[h]<i-T)++h; v=max(v,b[i]-b[q[h]]); while(h<=t&&b[q[t]]>=b[i-S+1])--t; q[++t]=i-S+1; } if(v<0)return 0; return 1; } int main() { //freopen("1.in","r",stdin); n=read();S=read();T=read(); for(int i=1;i<=n;++i) { a[i]=read(); maxx=max(maxx,a[i]); minn=min(minn,a[i]); } db l=minn,r=maxx; while(l+EPS<r) { db mid=(l+r)/2;; if(check(mid))l=mid; else r=mid; } printf("%.3lf",l); return 0; }
这道题目 想要让咱们去掉中间的连续子序列 使得剩下的平均奶产量最小,贪心显然没法解决这个问题。
仍是二分 可是 考虑一个问题上面两道题目是否等价的问题 一个两边平均值最小 一个中间平均值最大 遗憾的是随便出个数据便可说明显然是不等价的问题。
那么 我苦思冥想一中午 发现 这不是裸的二分么 我sb了...
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<ctime> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<algorithm> #include<vector> #include<cctype> #include<cstdlib> #include<utility> #include<bitset> #include<set> #include<map> #include<stack> #include<iomanip> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define db double #define EPS 1e-5 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n; int a[MAXN]; int minn=INF,maxx=-INF; db b[MAXN]; int q[MAXN],h,t; inline int check(db x) { db v=INF; h=1;t=0; for(int i=n;i>=1;--i)b[i]=a[i]-x+b[i+1]; for(int i=3;i<=n;++i) { while(h<=t&&b[q[t]]>=b[i])--t; q[++t]=i; } db cnt=0; for(int i=1;i<=n-2;++i) { cnt+=a[i]-x; while(h<=t&&q[h]<=i+1)++h; v=min(v,cnt+b[q[h]]); } if(v>0)return 0; return 1; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { a[i]=read(); maxx=max(maxx,a[i]); minn=min(minn,a[i]); } db l=minn,r=maxx; while(l+EPS<r) { db mid=(l+r)/2;; if(check(mid))r=mid; else l=mid; } printf("%.3lf",l); return 0; }
LINK:最大正方形增强版 每次都ban掉一个点求最大的正方形 前一次ban 对后一次有影响的那种。
怎么作 首先求最大的正方形 有两种方法 悬线法 或者直接 dp 惋惜 这两种方法都不支持动态的在图上更改 一旦更改就是n^2的 复杂度 n^2k 这显然不是咱们指望承受的复杂度。
考虑 怎么解决这个问题。 dp一旦修改死死的n^2 考虑悬线法求答案 动态ban点考虑动态加点ban点对答案影响太大 一旦ban掉一个答案 还得从新找一个答案很难操做 考虑倒序 加点。
而后 更新一下向左延伸和向右延伸的最大长度 求出以当前这个点为基准的最大正方形看可否更新答案便可整个复杂度为nk+n^2
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mod 1000000007 #define ull unsigned long long #define un unsigned #define P 1000000007ull using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { 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; } const int MAXN=2010; int n,m,k,cnt; char a[MAXN][MAXN]; struct wy{int x,y;}t[MAXN]; int l[MAXN][MAXN],r[MAXN][MAXN]; int f[MAXN][MAXN],ans[MAXN]; int L,R,q[MAXN],w[MAXN]; inline int check(int d,int y) { L=1;R=0; for(int i=1;i<=n;++i) { while(L<=R&&l[q[R]][y]>=l[i][y])--R; q[++R]=i; while(L<=R&&q[L]<=i-d)++L; w[i]=l[q[L]][y]; } L=1;R=0; for(int i=1;i<=n;++i) { while(L<=R&&r[q[R]][y]>=r[i][y])--R; q[++R]=i; while(L<=R&&q[L]<=i-d)++L; w[i]+=r[q[L]][y]-1; } for(int i=d;i<=n;++i)if(w[i]>=d)return 1; return 0; } int main() { //freopen("1.in","r",stdin); n=read();m=read();k=read(); for(int i=1;i<=n;++i)scanf("%s",a[i]+1); for(int i=1;i<=k;++i) { int x,y; x=read();y=read(); t[i]=(wy){x,y}; a[x][y]='X'; } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(a[i][j]=='X') { l[i][j]=0;f[i][j]=0; continue; } l[i][j]=l[i][j-1]+1; f[i][j]=min(f[i][j-1],min(f[i-1][j-1],f[i-1][j]))+1; cnt=max(cnt,f[i][j]); } for(int j=m;j;--j) { if(a[i][j]=='X')r[i][j]=0; else r[i][j]=r[i][j+1]+1; } } for(int i=k-1;i;--i) { ans[i+1]=cnt; int x=t[i+1].x; int y=t[i+1].y; a[x][y]='.'; for(int j=1;j<=m;++j)l[x][j]=a[x][j]=='X'?0:l[x][j-1]+1; for(int j=m;j;--j)r[x][j]=a[x][j]=='X'?0:r[x][j+1]+1; while(check(cnt+1,y))++cnt; } ans[1]=cnt; for(int i=1;i<=k;++i)printf("%d\n",ans[i]); return 0; }
LINK:统计距离问题很是巧妙的题目。
初看这道题以为很难作只能枚举坐标而后 前缀和什么的显然过不了 。考虑转换 画在图上一个点的可控范围是一个相似菱形的正方形 这种很难搞吧 咱们扫描线维护不了这么不规则的东西。
考虑切换成正方形! 怎么切换?发现转契比雪夫距离就变成了一个正方形的问题了 神奇 。而后呢咱们就是用一个边长为2k的正方形来 圈一些点 使其代价最大。
(窗口的星星可还行 而后就是把每一个点当成 一个正方形扫描线扫一下便可。
注意离散化 要不就平移坐标 可是动态开点标记永久化好像不太行的样子维护区间最值我也不太会。由于平移坐标暴力线段树了 因此空间消耗大会T。
最后仍是离散了一下。
//单个局面偶数必败 //以为单个局面除了1 剩下也是必败 直接异或好了 //#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum #define tag(p) t[p].tag #define zz p<<1 #define yy p<<1|1 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010<<1; int n,k,cnt,top,ans; struct wy { int l,r; int tag; int sum; }t[MAXN<<2],w[MAXN]; int b[MAXN]; inline int cmp(wy a,wy b){return a.l<b.l;} inline void build(int p,int l,int r) { l(p)=l;r(p)=r; if(l==r)return; int mid=(l+r)>>1; build(zz,l,mid); build(yy,mid+1,r); } inline void pushdown(int p) { sum(zz)+=tag(p); sum(yy)+=tag(p); tag(zz)+=tag(p); tag(yy)+=tag(p); tag(p)=0; return; } inline void change(int p,int l,int r,int x) { if(l<=l(p)&&r>=r(p)) { sum(p)+=x; tag(p)+=x; return; } int mid=(l(p)+r(p))>>1; if(tag(p))pushdown(p); if(l<=mid)change(zz,l,r,x); if(r>mid)change(yy,l,r,x); sum(p)=max(sum(zz),sum(yy)); } inline void discrete() { sort(b+1,b+1+cnt); for(ll i=1;i<=cnt;++i)if(i==1||b[i]!=b[i-1])b[++top]=b[i]; for(ll i=1;i<=cnt;++i) { w[i].r=lower_bound(b+1,b+1+top,w[i].r)-b; w[i].tag=lower_bound(b+1,b+1+top,w[i].tag)-b; } } int main() { freopen("1.in","r",stdin); n=read();k=read()<<1; for(int i=1;i<=n;++i) { int x,y,z; z=read();x=read();y=read(); w[++cnt].l=x+y;b[cnt]=w[cnt].r=x-y; w[cnt].tag=x-y+k;w[cnt].sum=z; w[++cnt].l=x+y+k+1;w[cnt].r=x-y; w[cnt].tag=x-y+k;w[cnt].sum=-z; b[cnt]=x-y+k; } discrete(); sort(w+1,w+1+cnt,cmp); build(1,1,top); for(int i=1;i<=cnt;++i) { change(1,w[i].r,w[i].tag,w[i].sum); //cout<<w[i].l<<endl; //cout<<w[i].sum<<' '<<sum(1)<<endl; while(w[i].l==w[i+1].l&&i+1<=cnt) { ++i; change(1,w[i].r,w[i].tag,w[i].sum); //cout<<w[i].sum<<' '<<sum(1)<<' '<<endl; } ans=max(ans,sum(1)); } printf("%d\n",ans); return 0; }