zoukankan      html  css  js  c++  java
  • Codeforces 1181

    1181 B

    题意

    一个高精整数,要把它从中间割开分成两部分,每一部分不能为空也不能有前导零,问如何才能使得两部分的和最小。

    Examples

    input
    7
    1234567
    output
    1801
    input
    3
    101
    output
    11

    打高精好点烦的
    就均分成两半,再判一判前导零就好了。

    1181 C

    题意

    旗帜的定义:
    这些是旗帜:


    这些不是旗帜:





    现在给你一个英文字母矩阵,问有多少个子矩形是旗帜。 ((n,mle 1000))

    Examples

    input
    4 3
    aaa
    bbb
    ccb
    ddd
    output
    6
    input
    6 1
    a
    a
    b
    b
    c
    c
    output
    1
    input
    4 3
    aca
    ccc
    bbb
    aaa
    output
    8

    弄一个双端队列模拟即可。
    细节较多。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<char,int> P;
    const int maxn=1003,INF=1050000000;
    char s[maxn];
    int n,m;
    long long ans;
    deque<P> a[maxn];
    bool valid(const deque<P> &q){
    	return q.size()==3&&q[0].second>=q[1].second&&q[1].second==q[2].second;
    }
    bool equal(const deque<P> &q1,const deque<P> &q2){
    	if(q2.size()!=3)return 0;
    	for(int i=0;i<=2;i++)if(q1[i].first!=q2[i].first)return 0;
    	return q1[1].second==q2[1].second&&q1[2].second==q2[2].second;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%s",s+1);
    		for(int j=1;j<=m;j++){
    			if(a[j].size()>=1&&a[j].back().first==s[j])a[j].back().second++;
    			else a[j].push_back(P(s[j],1));
    			while(a[j].size()>=2&&a[j].back().second>a[j][a[j].size()-2].second)a[j].pop_front();
    			if(a[j].size()>=4)a[j].pop_front();
    		}
    		for(int l=1,r=1;r<=m;l=r){
    			for(;l<=m&&!valid(a[l]);l++);
    			for(r=l;r<=m&&valid(a[r])&&equal(a[l],a[r]);r++);
    			ans+=(long long)(r-l)*(r-l+1)/2;
    		}
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    1181 D

    题意

    (m) 个城市,每年举办比赛。在最先的 (n) 年中比赛的举办地是已知的,后来制定了规则,每年以每个城市之前举办次数为第一关键字,以每个城市的编号为第二关键字,选择最小的城市举办比赛。现在有 (q) 个询问,每次询问第 (x_i) 年由哪个城市举办。 ((1le n,m,qle 5*10^5,x_ile 10^{18}))

    Examples

    input
    6 4 10
    3 1 1 1 2 2
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    output
    4
    3
    4
    2
    3
    4
    1
    2
    3
    4
    input
    4 5 4
    4 4 5 1
    15
    9
    13
    6
    output
    5
    3
    3
    3

    模拟发现,经过 (O(n^2)) 年,举办城市必定会变成 (1,2,dots ,m) 的顺序。
    因此,在临界值之后的询问很容易得出结果。
    在临界值之前的询问呢?
    考虑将每个城市按上述两个关键字排序,然后对于每个第一关键字相等的区间设它们为 ([l_i...r_i](r_i+1=l_{i+1},l_1=1)) 我们发现举办城市的顺序就是:

    • (1)(r_1) 轮流,进行 (cnt[l_2]-cnt[1]) 遍;
    • (dots)
    • (1)(r_{i+1}) 轮流,进行 (cnt[l_{i+2}]-cnt[l_{i+1}]) 遍;
    • (dots)
    • (1)(r_{k-1}) 轮流,进行 (cnt[l_k]-cnt[l_{k-1}]) 遍;
    • (1)(m) 轮流,进行 (∞) 遍(这是临界值之后)。

    每次相当于一轮轮完之后把 ([1,r_i])([l_{i+1},r_{i+1}]) 合并。
    (n^2) 归并显然是不行的,因此我们用一种数据结构来实现每个城市的名次查询。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef pair<int,int> P;
    typedef pair<long long,int> QQ;
    const int maxn=500003,INF=1050000000;
    P a[maxn];
    QQ q[maxn];
    int n,m,Q,ans[maxn],t[maxn<<2];
    int mod(long long x,int y){return x%y==0?y:x%y;}
    void change(int p,int l,int r,int pos){
    	if(l==r){
    		t[p]++;
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(pos<=mid)change(p<<1,l,mid,pos);
    	else change(p<<1|1,mid+1,r,pos);
    	t[p]=t[p<<1]+t[p<<1|1];
    }
    int kth(int p,int l,int r,int rnk){
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	if(rnk<=t[p<<1])return kth(p<<1,l,mid,rnk);
    	else return kth(p<<1|1,mid+1,r,rnk-t[p<<1]);
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&Q);
    	for(int i=1;i<=n;i++){
    		int x;
    		scanf("%d",&x);
    		a[x].first++;
    	}
    	for(int i=1;i<=m;i++)a[i].second=i;
    	sort(a+1,a+m+1);
    	for(int i=1;i<=Q;i++)scanf("%lld",&q[i].first),q[i].second=i;
    	sort(q+1,q+Q+1);
    	int l,r,pos;
    	long long now=n,nxt;
    	for(l=1,r=1,pos=1;;l=r){
    		for(;r<=m&&a[r].first==a[l].first;r++)change(1,1,m,a[r].second);
    		if(r>m)break;
    		nxt=now+(long long)(a[r].first-a[l].first)*(r-1);
    		for(;pos<=Q&&q[pos].first<=nxt;pos++){
    			ans[q[pos].second]=kth(1,1,m,mod(q[pos].first-now,r-1));
    		}
    		now=nxt;
    	}
    	for(;pos<=Q;pos++){
    		ans[q[pos].second]=mod(q[pos].first-now,m);
    	}
    	for(int i=1;i<=Q;i++)printf("%d
    ",ans[i]);
    	return 0;
    }
    

    1181 E1

    题意

    考虑题目描述的逆过程,不难发现就是一个 (O(n^2log n)) 的分治。即对于每个状态,找到一条分割线,使得这条线不穿过任何一个块。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1003;
    struct T{
    	int u,d,l,r;
    }a[maxn];
    int n;
    bool cmp1(T x,T y){return x.l!=y.l?x.l<y.l:x.r<y.r;}
    bool cmp2(T x,T y){return x.d!=y.d?x.d<y.d:x.u<y.u;}
    bool solve(int L,int R){
    	if(L==R)return 1;
    	int p=-1;
    	sort(a+L,a+R+1,cmp1);
    	for(int i=L+1,mx=0;i<=R;i++){
    		mx=max(mx,a[i-1].r);
    		if(mx<=a[i].l){p=i;break;}
    	}
    	if(~p)return solve(L,p-1)&&solve(p,R);
    	sort(a+L,a+R+1,cmp2);
    	for(int i=L+1,mx=0;i<=R;i++){
    		mx=max(mx,a[i-1].u);
    		if(mx<=a[i].d){p=i;break;}
    	}
    	if(~p)return solve(L,p-1)&&solve(p,R);
    	return 0;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d%d%d%d",&a[i].l,&a[i].d,&a[i].r,&a[i].u);
    	puts(solve(1,n)?"YES":"NO");
    	return 0;
    }
    

    1181 E2

    题意

    接着上题的做法。考虑启发式算法。我们维护四个链表/set,从上到下、从下到上、从左到右、从右到左“多线程”地寻找满足要求的分割线,找到后立即break,设总共遍历了 (4x) 个元素,然后以 (O(x)) 的复杂度将四个数据结构按在分割线左(上)侧还是右(下)侧分成两部分,然后递归处理。复杂度 (O(nlog^2 n)) 。链表太难调了。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=100003,INF=1050000000;
    struct T{
    	int l,d,r,u;
    	T(){}
    	T(int _l,int _d,int _r,int _u):l(_l),d(_d),r(_r),u(_u){}
    }b[maxn];
    struct node{
    	int x,y,num;
    	node(){}
    	node(int _x,int _y,int _n):x(_x),y(_y),num(_n){}
    	bool operator<(node t)const{return x!=t.x?x<t.x:y<t.y;}
    }a[4][maxn];
    int n,tmp[maxn],j;
    bool cmp(int x,int y){return a[j][x]<a[j][y];}
    struct list{
    	int pre,nxt;
    }lst[4][maxn];
    #define L lst[j]
    bool solve(){
    	if(lst[0][lst[0][0].nxt].nxt==0)return 1;
    	int mx[4]={-INF,-INF,-INF,-INF},q=-1,tmphead[4],now[4],now1[4];
    	for(j=0;j<=3;j++)now[j]=L[0].nxt,now1[j]=L[now[j]].nxt;
    	while(1){
    		if(now1[0]==0)break;
    		for(j=0;j<=3;j++){
    			mx[j]=max(mx[j],a[j][now[j]].y);
    			if(mx[j]<=a[j][now1[j]].x){
    				q=j;
    				tmphead[q]=now1[j];
    				L[now[j]].nxt=L[now1[j]].pre=0;
    				break;
    			}
    			now[j]=now1[j],now1[j]=L[now1[j]].nxt;
    		}
    		if(~q)break;
    	}
    	if(q==-1)return 0;
    	for(j=0;j<=3;j++){
    		if(j!=q){
    			int cnt=0;
    			for(int i=lst[q][0].nxt;i;i=lst[q][i].nxt){
    				L[L[i].pre].nxt=L[i].nxt;
    				if(L[i].nxt)L[L[i].nxt].pre=L[i].pre;
    				L[i].pre=L[i].nxt=0;
    				tmp[++cnt]=i;
    			}
    			tmphead[j]=L[0].nxt;
    			sort(tmp+1,tmp+cnt+1,cmp);
    			for(int i=0;i<cnt;i++)L[tmp[i]].nxt=tmp[i+1],L[tmp[i+1]].pre=tmp[i];
    		}
    	}
    	bool ret=solve();
    	if(!ret)return 0;
    	for(int j=0;j<=3;j++){
    		L[0].nxt=tmphead[j];
    		L[tmphead[j]].pre=0;
    	}
    	ret=solve();
    	if(!ret)return 0;
    	return 1;
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		int l,d,r,u;
    		scanf("%d%d%d%d",&l,&d,&r,&u);
    		b[i]=T(l,d,r,u);
    		a[0][i]=node(l,r,i);
    		a[1][i]=node(-r,-l,i);
    		a[2][i]=node(d,u,i);
    		a[3][i]=node(-u,-d,i);
    	}
    	for(j=0;j<=3;j++){
    		for(int i=1;i<=n;i++)tmp[i]=i;
    		sort(tmp+1,tmp+n+1,cmp);
    		for(int i=0;i<n;i++)L[tmp[i]].nxt=tmp[i+1],L[tmp[i+1]].pre=tmp[i];
    	}
    	puts(solve()?"YES":"NO");
    	return 0;
    }
    
  • 相关阅读:
    常用排序算法(JAVA版)
    常用排序算法(PHP)
    POJ 1308 hdu 1325 Is It A Tree?【并查集+入度 判断一个有向图是树】
    【转】常用的正则表达式
    POJ 1611 The Suspects【并查集入门】
    POJ 2524 Ubiquitous Religions【并查集入门】
    归并排序和快速排序比较【算法设计与分析实验报告】
    2013_CSUST_3_23校内训练赛第一场【old】【hdu 3496、2191、4508、4506、2181 POJ 3264 3210 3094】
    POJ 3264 Balanced Lineup 【RMQ求区间最值模板题】
    搜索专题训练【CSUST_Newer_12级入门】
  • 原文地址:https://www.cnblogs.com/BlogOfchc1234567890/p/11051532.html
Copyright © 2011-2022 走看看