• JOI 2017 Final题解


    T1

    把修改放到差分序列上做,这样每次修改只需要修改两个位置,且每次修改会对后面的所有温度产生影响

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m,a[200200];
    ll f[200100];
    ll s,t,ans;
    
    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;
    }
    
    void modify(int pos,ll val)
    {
    	if (pos>n) return;
    	if (f[pos]>0) ans+=s*f[pos];
    	else ans+=t*f[pos];
    	f[pos]+=val;
    	if (f[pos]>0) ans-=s*f[pos];
    	else ans-=t*f[pos];
    }
    
    signed main()
    {
    	n=read();m=read();s=read();t=read();
    	rep(i,0,n) a[i]=read();
    	rep(i,1,n) f[i]=a[i]-a[i-1];
    	ans=0;
    	rep(i,1,n)
    		if (f[i]>0) ans-=s*f[i];
    		else ans-=t*f[i];
    	while (m--)
    	{
    		int l=read(),r=read(),x=read();
    		modify(l,x);modify(r+1,-x);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    

    T2

    不难发现如果按照“快车->准快车->慢车”的顺序坐车一定不会更劣

    对于每一段快车区间((s_i,s_{i+1}]),考虑如何求出最大的可到达车站

    我们在(s_i)下车后会有一段剩余时间,记作(rst_i),和最开始的结论同理,我们希望在“最后一个可以使用慢车到达的车站”处建立一个准快车车站,使得从(s_i)可以直接到达那里,证明的话考虑往前或往后调整都不会是更优的

    这样的话我们就有(O(mk))个可以考虑的准快车建立点,使用优先队列贪心即可

    注意一些奇奇怪怪的特判

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define int long long
    #define eps 1e-8
    int n,m,k,now[5050],s[5050];
    ll a,b,c,tot,rst[5050];
    priority_queue<int> q;
    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;
    }
    
    signed main()
    {
    	n=read();m=read();k=read();
    	scanf("%lld%lld%lld%lld",&a,&b,&c,&tot);
    	rep(i,1,m) 
    	{
    		s[i]=read();
    		rst[i]=tot-b*(s[i]-1);
    		now[i]=s[i];
    	}
    	int ans=0;
    	//rep(i,1,m) cout << now[i] << " ";cout << endl;
    	if (b*(s[m]-1)>tot) ans--;
    	rep(i,1,m-1)
    	{
    		if (rst[i]<0) continue;
    		int nxt=min(s[i+1],s[i]+rst[i]/a+1);
    		ans+=(nxt-s[i]);
    		rst[i]-=c*(nxt-s[i]);
    		now[i]=nxt;
    	}
    	rep(i,1,k)
    	{
    		rep(j,1,m-1)
    		{
    			if (rst[j]<0) continue;
    			int nxt=min(s[j+1],now[j]+rst[j]/a+1);
    			q.push(nxt-now[j]);
    			rst[j]-=c*(nxt-now[j]);
    			now[j]=nxt;
    		}
    	}
    	rep(i,1,k-m)
    	{
    		if (q.empty()) break;
    		ans+=q.top();q.pop();
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    T3

    画一下的话可以发现合法方案就是在四个角上的阶梯形,由于四种情况等价我们可以将矩阵旋转后只处理左下角的阶梯形,最后取个(min)即可

    题目中由很明显的二分答案的暗示,但是看起来(check)不太容易

    考虑矩阵的全局(max)(min)值,对于我们二分出来的(mid),我们考虑左下角的阶梯有(geq mid+min),右上角的有(leq max-mid),由于(max)(min)不会在同一个阶梯里,所以这样做我们一定能考虑到最优解,之后维护一个当前行的右端点指针(单调不降),每行判断一下在左下角不合法的点是否在右上角即可

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 2e9
    #define eps 1e-8
    int n,m,a[2020][2020],ans=maxd,mx=0,mn=maxd;
    
    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;
    }
    
    void reverse1()
    {
    	rep(i,1,n/2) rep(j,1,m) swap(a[i][j],a[n-i+1][j]);
    }
    
    void reverse2()
    {
    	rep(i,1,n) rep(j,1,m/2) swap(a[i][j],a[i][m-j+1]);
    }
    
    bool chk(int mid)
    {
    	int now=0;
    	rep(i,1,n)
    	{
    		rep(j,1,m)
    			if (a[i][j]<mx-mid) now=max(now,j);
    		rep(j,1,m)
    			if ((a[i][j]>mid+mn) && (j<=now)) return 0;
    	}
    	return 1;
    }
    
    void solve()
    {
    	int l=0,r=mx-mn,now=0;
    	while (l<=r)
    	{
    		int mid=(l+r)>>1;
    		if (chk(mid)) {now=mid;r=mid-1;}
    		else l=mid+1;
    	}
    	ans=min(ans,now);
    }
    
    int main()
    {
    	n=read();m=read();
    	rep(i,1,n) rep(j,1,m)
    	{
    		a[i][j]=read();
    		mx=max(a[i][j],mx);
    		mn=min(a[i][j],mn);
    	}
    	solve();
    	reverse1();
    	solve();
    	reverse2();
    	solve();
    	reverse1();
    	solve();
    	printf("%d",ans);
    	return 0;
    }
    

    T4

    对于每名球员,最优策略一定是:跑到某个位置接球->带着球跑->踢出

    也就是说当一个球到达某个点的时候,一定是一个距离它最近的人跑过去捡起它

    接下来考虑拆点跑最短路,((x,y,0-3))表示一个球在往四个方向走,((x,y,4))表示有人在((x,y))控球

    首先球往四个方向跑的时候花费是(a),之后一个人在控球状态下如果将球提出可以看做(4)(0-3)连了一条长度为(b)的边,之后带球跑动便连(c)的边,最后如果一个球在某一个点要想找一个控球人的话,就是离它最近的人的距离( imes c),这个可以通过预先bfs实现

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    const int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
    struct node{int to,nxt;ll cost;}sq[5000100];
    int all=0,head[1501000];
    struct hnode{int u;ll dis;};
    bool operator <(hnode p,hnode q) {return p.dis>q.dis;}
    struct pnode{int x,y;};
    queue<pnode> q;
    int h,w,n,id[510][510][5],a,b,c,tot=0;
    ll d[510][510],dis[1500010];
    bool vis[1500010],in[505][505];
    
    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;
    }
    
    void add(int u,int v,ll w)
    {
    	all++;sq[all].to=v;sq[all].nxt=head[u];sq[all].cost=w;head[u]=all;
    }
    
    void bfs()
    {
    	while (!q.empty())
    	{
    		pnode now=q.front();q.pop();
    		rep(i,0,3)
    		{
    			int nx=now.x+dx[i],ny=now.y+dy[i];
    			if ((nx<1) || (nx>h) || (ny<1) || (ny>w)) continue;
    			if (d[nx][ny]>d[now.x][now.y]+c)
    			{
    				d[nx][ny]=d[now.x][now.y]+c;
    				q.push((pnode){nx,ny});
    			}
    		}
    	}
    }
    
    void dij(int st)
    {
    	priority_queue<hnode> q;
    	while (!q.empty()) q.pop();
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	dis[st]=0;q.push((hnode){st,0});
    	while (!q.empty())
    	{
    		int u=q.top().u;q.pop();
    		if (vis[u]) continue;vis[u]=1;
    		for (int i=head[u];i;i=sq[i].nxt)
    		{
    			int v=sq[i].to;
    			if (dis[v]>dis[u]+sq[i].cost)
    			{
    				dis[v]=dis[u]+sq[i].cost;
    				if (!vis[v]) q.push((hnode){v,dis[v]});
    			}
    		}
    	}
    }
    
    int main()
    {
    	h=read()+1;w=read()+1;
    	a=read();b=read();c=read();
    	n=read();
    	rep(i,1,h) rep(j,1,w) rep(k,0,4) id[i][j][k]=(++tot);
    	//cout << tot << endl;
    	int stx,sty,edx,edy;
    	memset(d,0x3f,sizeof(d));
    	rep(i,1,n)
    	{
    		int x=read()+1,y=read()+1;
    		if (i==1) {stx=x;sty=y;}
    		if (i==n) {edx=x;edy=y;}
    		if (!in[x][y]) q.push((pnode){x,y});
    		in[x][y]=1;d[x][y]=0;
    	}
    	bfs();
    	rep(x,1,h)
    		rep(y,1,w)
    		{
    			rep(k,0,3)
    			{
    				add(id[x][y][k],id[x][y][4],d[x][y]);
    				add(id[x][y][4],id[x][y][k],b);
    				int nx=x+dx[k],ny=y+dy[k];
    				if ((nx<1) || (nx>h) || (ny<1) || (ny>w)) continue;
    				add(id[x][y][k],id[nx][ny][k],a);
    				add(id[x][y][4],id[nx][ny][4],c);
    			}
    		}
    	dij(id[stx][sty][4]);
    	printf("%lld
    ",dis[id[edx][edy][4]]);
    	return 0;
    }
    

    T5

    首先显然的是我们可以先完成所有的染色,再进行折叠

    将最后的序列还原,不难发现一个合法的序列是有两种颜色的连续段,且除了开头和结尾的两段长度奇偶性任意以外,其它的每一段长度均为偶数

    直接做依然不大好做,注意到满足上一条件的某种颜色段的起始位置奇偶性一致,那么我们可以枚举这个奇偶性,之后调整该颜色使其合法(具体如何调整见程序),之后我们可以贪心的选取剩下最多个数的颜色作为第二种颜色。

    #include<iostream>
    #include<string.h>
    #include<string>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<math.h>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    typedef long double db;
    typedef pair<int,int> pii;
    const int N=10000;
    const db pi=acos(-1.0);
    #define lowbit(x) (x)&(-x)
    #define sqr(x) (x)*(x)
    #define rep(i,a,b) for (register int i=a;i<=b;i++)
    #define per(i,a,b) for (register int i=a;i>=b;i--)
    #define fir first
    #define sec second
    #define mp(a,b) make_pair(a,b)
    #define pb(a) push_back(a)
    #define maxd 998244353
    #define eps 1e-8
    int n,m,col[1001000],mx,cnt[1001000],cnt1[1001000];
    vector<int> pos[1001000];
    
    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;
    }
    
    void add(int c)
    {
    	cnt[cnt1[c]]--;cnt1[c]++;cnt[cnt1[c]]++;
    	if (cnt1[c]>mx) mx=cnt1[c];
    }
    
    void dec(int c)
    {
    	cnt[cnt1[c]]--;cnt1[c]--;cnt[cnt1[c]]++;
    	if (!cnt[mx]) mx--;
    }
    
    int main()
    {
    	n=read();m=read();
    	rep(i,1,n)
    	{
    		col[i]=read();add(col[i]);
    		pos[col[i]].pb(i);
    	}
    	rep(i,1,m)
    	{
    		int len=pos[i].size();
    		col[0]=col[n+1]=i;
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j];
    			dec(i);
    		}
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j],nxt;
    			if (now&1) nxt=now-1;else nxt=now+1;
    			if (col[nxt]!=i) dec(col[nxt]);
    		}
    		int ans=n-len-mx;
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j],nxt;
    			if (now&1) nxt=now-1;else nxt=now+1;
    			if (col[nxt]!=i) add(col[nxt]);
    		}
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j],nxt;
    			if (now&1) nxt=now+1;else nxt=now-1;
    			if (col[nxt]!=i) dec(col[nxt]);
    		}
    		ans=min(ans,n-mx-len);
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j],nxt;
    			if (now&1) nxt=now+1;else nxt=now-1;
    			if (col[nxt]!=i) add(col[nxt]);
    		}
    		printf("%d
    ",ans);
    		rep(j,0,len-1)
    		{
    			int now=pos[i][j];
    			add(i);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    WINDOWS API ——GETFILETIME——获取文件时间
    lua 源码分析之线程对象lua_State
    GPL、BSD、MIT、Mozilla、Apache、LGPL开源协议介绍
    BOOST 线程完全攻略
    宏定义中#和##符号的使用和宏定义展开问题
    weak_ptr<T>智能指针
    轻松记住大端小端的含义(附对大端和小端的解释)
    关于VC预定义常量_WIN32,WIN32,_WIN64
    VC 预定义宏
    php技术之路
  • 原文地址:https://www.cnblogs.com/encodetalker/p/11817282.html
Copyright © 2020-2023  润新知