第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(昆明)

H Hard Calculation

签到题

J Parallel Sort

分析:很好想到找环 对于每个环 最多两次操作即可还原

构造每个环的方案 :每次将环脱去一对即可

开始我构造的按照顺序脱去一对 但是只过了70个点 正解为首尾依次脱环

#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;

struct huan
{
    vector<int> sig;//环中数据的位置
    int type;//环的型号,一元环为0,2元为1,更多为2
};

int maxtip = 0;//最大环型号
huan allh[100005];//环组
int aback = 0;//环组尾标
int orig[100005];//原始数据
int seat[100005];//i在原始数据中的位置
bool used[100005];//开环时被使用标记

int main()
{
    int n; cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> orig[i];
        seat[orig[i]] = i;
    }

    //打出所有环
    for (int i = 1; i <= n; i++)
    {
        if (used[i] == 1)
            continue;//已经入环的去掉
        int t = aback;
        aback++;//栈顶移动
        allh[t].sig.push_back(i);//环的开头
        used[i] = 1;
        int j = orig[i];
        while (1)
        {
            if (j != i)//不是环尾
            {
                allh[t].sig.push_back(j);
                used[j] = 1;
                j = orig[j];
            }
            else//是环尾
            {
                used[j] = 1;
                break;//不进入,直接跳出
            }
        }

        //给出类型
        int size = allh[t].sig.size();
        if (size == 1)
            allh[t].type = 0;
        else if(size == 2)
            allh[t].type = 1;
        else
            allh[t].type = 2;
        maxtip = max(maxtip, allh[t].type);
    }

    //输出结果
    cout << maxtip << endl;
    for (int i = maxtip; i > 0; i--)//每一行
    {
        vector<int> y;//装输出数据的数组
        for (int j = 0; j < aback; j++)//遍历每个环
        {
            if (allh[j].type >= i)//只有等级足够的环才来
            {
                int right = allh[j].sig.size() - 1;
                int left = i == 2 || allh[j].type == 1 ? 0 : 1;
                while (left < right)
                {
                    y.push_back(allh[j].sig[left]);
                    y.push_back(allh[j].sig[right]);
                    left++;
                    right--;
                }
            }
        }
        cout << y.size() / 2;
        for (int k = 0; k < y.size(); k++)
        {
            cout << " " << y[k];
        }
        cout << endl;
    }
}


L Simone and graph coloring

分析:考虑如果完全降序 整个图即为一个完全图 这样每个点的颜色都需要不同

所以想到对于每个下降子序列的长度就是所需颜色的数量 最长就是答案

至于方案数在求最长下降子序列的过程中跟新

需要用到二分dp来求解

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,n,a[N],b[N],ls[N];

int get(int l,int r,int x){
	while(l<r){
		int mid=(l+r)>>1;
		if(ls[mid]>x) l=mid+1;
		else r=mid;
	}
	return l;
}

void solve(){
	int tot=0; cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		if(tot==0||a[i]<ls[tot]) ls[b[i]=++tot]=a[i];
		else{
			int tmp = get(1,tot,a[i]);
			b[i] = tmp;
			ls[tmp] = a[i];
		}
	}
	cout<<tot<<"\n";
	for(int i=1;i<=n;i++) cout<<b[i]<<" \n"[i==n];
}

int main(){
	cin>>T;
	while(T--) solve();
}

M Stone Games

题意:求静态区间数的选取集合不能构造出的和的最小值,在线

分析:
如果没有1,则凑不出来的最小数为1
否则设有x个1
显然,一定可以凑出[1,x]
注意到如果剩余大于1的数中最小数为k,若k<=x+1
则可以凑出[1,x+k]

推知若当前可以凑出[1,x],剩余数中所有小于等于x+1的数,均可累加到连续上限中
即凑出[1,x+Σsi(si<=x+1)]
若找不到新增的数,则答案为x+1。

主席树维护区间小于等于某个数的和,可以不初始化直接边插入边建树

#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long

using namespace std;
const int N = 1e6+10,inf = 1e9;

int n,m,idx,root[N];

struct Node{
    int l,r;
    int sum;
}tr[N*50];

int insert(int p,int l,int r,int x){
    int q=++idx;
    tr[q]=tr[p];
    if(l==r){
        tr[q].sum+=x;
        return q;
    }
    int mid=l+r>>1;
    if(x<=mid) tr[q].l=insert(tr[p].l,l,mid,x);
    else tr[q].r=insert(tr[p].r,mid+1,r,x);
    tr[q].sum=tr[tr[q].l].sum+tr[tr[q].r].sum;
    return q;
}

int query(int q,int p,int l,int r,int ql,int qr){
    if(l>=ql&&r<=qr) return tr[q].sum-tr[p].sum;
    if(l>qr||r<ql) return 0;
    int mid=l+r>>1;
    return query(tr[q].l,tr[p].l,l,mid,ql,qr)+query(tr[q].r,tr[p].r,mid+1,r,ql,qr);
}

signed main(){
    cin>>n>>m;

    for(int i=1,t;i<=n;i++){
        cin>>t;
        root[i]=insert(root[i-1],0,inf,t);
    }

    int res=0;

    while(m--){
        int l,r;
        cin>>l>>r;
        l=(res+l)%n+1,r=(res+r)%n+1;
        if(l>r) swap(l,r);
        int x=query(root[r],root[l-1],0,inf,1,1);
        int last=x;
        while(1){
            int sum=query(root[r],root[l-1],0,inf,1,min(inf,x+1))-last;
            if(!sum) break;
            x+=sum,last=x;
        }
        res=x+1;
        cout<<res<<endl;
    }

    return 0;
}

J Mr. Main and Windmills

分析:就是暴力就好 计算几何不在我的范围类

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
LL x1,y1,x2,y2;
LL  n,m;
struct node
{
	LL x;
	LL y;
}N[1001];
vector<double> V[1001];
int main()
{
	cin>>n>>m;
	cin>>x1>>y1>>x2>>y2;
	for(int a=1;a<=n;a++) cin>>N[a].x>>N[a].y;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=n;j++)
	 if(i!=j)
	 {
	 	LL x3=N[i].x,y3=N[i].y,x4=N[j].x,y4=N[j].y;
	 	double t1=(double)(x3 * (y4 - y3) + y1 * (x4 - x3) - y3 * (x4 - x3) - x1 * (y4 - y3)) / ((x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1));
	 	if(t1>0&&t1<1) V[i].push_back(t1);
	 }
	 for(int a=1;a<=n;a++) sort(V[a].begin(),V[a].end());
	while(m--)
	{
		int h,k;
		cin>>h>>k;
		if(k<=V[h].size()) printf("%.6f %.6f\n",x1+V[h][k-1]*(x2-x1),y1+V[h][k-1]*(y2-y1));
		else cout<<-1<<"\n";
	}	
}

C Cities

题意:

给定长度为n的序列a 一次操作你可以选择一个数值相同的连续区间,将这个区间的数值修改为其他数。问多少进行多少次操作能使得区间所有数相同。

数据范围:n<=5000,每种a(i)最多出现15次

分析:

很明显的区间dp 关键是数据范围只允许15×n方 但是一般的区间dp范围时n三方

考虑将第三维 枚举中间断点的变成最多枚举十五次 想到维护每个数前面和她颜色一样的数的位置 用pre[i] 表示

首先对序列进行去重 这一步很关键!!!!

因为只有两端相同的情况下 才会使得区间答案变得更小

d[l][r]可以由多种状态转移更新:

1.首先用d[l+1][r]+1和a[l][r-1]+1更新,这个转移比较显然.

2.如果a[l]=a[r],那么可以用d[l+1][r-1]+1更新,将[l+1,r-1]变成相同,再变成a[r].

!!!!但是这样不一定是最优的 因为可能[l+1,r-1]开始就变成了a[r] 就不需要再花费1了 没关系取个min就好

首先明确 d[l][r] 如果a[l]=a[r] 最终这段区间一定是a[r] 如果a[l]!=a[r] 最终这段区间可能为区间内任何出现过的数 并且如果最终是a[l] 或者a[r]都是一样的

3.对于[l+1,r-1]中的位置k,如果满足a[k]=a[r],那么可以用d[l][k-1]+d[k][r]+1更新

4.对于[l+1,r-1]中的位置k,如果满足a[k]=a[r],同时a[l]=a[k],即a[l]=a[k]=a[r]

那么可以用d[l][k-1]+d[k][r]更新,即将[l,k-1]和[k+1,r]都变成a[k].

最后d[1][n]就是答案.

说难也不难 说简单也真不简单

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
#define ll long long
const int maxn=5e3+5;
int n,cnt;
int dp[maxn][maxn],a[maxn],pre[maxn],mp[maxn];
void solve();
int main(){
	int T;cin>>T;
	while(T--)solve();
     return 0;
}
void solve(){
	cin>>n;cnt=0;
	memset(dp,0,sizeof(dp));
	memset(pre,0,sizeof(pre));
	memset(mp,0,sizeof(mp));
	//去重这一步非常很关键 
	for(int i=1;i<=n;i++){
	    scanf("%d",&a[i]);
		if(a[i]!=a[i-1])
		a[++cnt]=a[i];
		else continue;
		pre[cnt]=mp[a[i]];
		mp[a[i]]=cnt;
	}
	for(int i=1;i<=cnt;i++)
       for(int j=i;j<=cnt;j++)
	dp[i][j]=j-i;
	
	for(int len=2;len<=cnt;len++){
		for(int l=1;l+len-1<=cnt;l++){
			int r=l+len-1;
			dp[l][r]=min(dp[l][r],min(dp[l+1][r],dp[l][r-1])+1);
			if(a[l]==a[r])dp[l][r]=min(dp[l][r],dp[l+1][r-1]+1);
			for(int k=pre[r];k>l;k=pre[k]){
				dp[l][r]=min(dp[l][r],dp[l][k-1]+dp[k][r]+1);
		       if(a[l]==a[r])
		       dp[l][r]=min(dp[l][r],dp[l][k-1]+dp[k][r]);
			}
		}
	}
	cout<<dp[1][cnt]<<endl;
}
posted @ 2022-10-26 18:20  wzx_believer  阅读(119)  评论(0)    收藏  举报