The 2024 ICPC Kunming Invitational Contest

 tot:这场赛时出了4题,都是签到题,没什么难度。然后补出来了三题,共七题。本来想着再补一题L.Trails--但是没补出来,先这样吧。

https://codeforces.com/gym/105386/problem/E--RelearnThroughReview

思路:

得"发现"前缀gcd的种类数是不多的.2*3*4*5*6*7...增长的非常快.
那么可以枚举gcd的种类,并且每种种类取最长的前缀.然后依次从后枚举改多少个,再维护一个后缀gcd来优化.
int n,k;
//得"发现"前缀gcd的种类数是不多的.2*3*4*5*6*7...增长的非常快.
//那么可以枚举gcd的种类,并且每种种类取最长的前缀.然后依次从后枚举改多少个,再维护一个后缀gcd来优化.
//Relearn through Review
//https://codeforces.com/gym/105386/problem/E
void solve(){       //枚举+贪心
    cin>>n>>k;
    vector<int> arr(n+10);
    vector<int> suf(n+10,0);
    for(int i=1;i<=n;i++) cin>>arr[i];
    for(int i=n;i>=1;i--) suf[i]=gcd(suf[i+1],arr[i]);
    int ans=suf[1],gccd=0;
    令人害怕的双重循环..但是实际上第二重循环执行的次数是不到20次的;
    因为2*3*4*5*6*7...增长的非常快.gcd的种类数不多
    for(int i=1;i<=n;i++){
        if(gccd==gcd(gccd,arr[i])) continue;
        //第一次是从头开始枚举修改.
        int temp=gccd;
        for(int j=i;j<=n;j++) temp=gcd(temp,arr[j]+k),ans=max(ans,gcd(temp,suf[j+1]));
        gccd=gcd(gccd,arr[i]);
    }
    cout<<ans<<endl;
}

https://codeforces.com/gym/105386/problem/M--ItalianCuisine

思路:类似旋转卡壳的双指针枚举,主要是注意实现的细节,思维上不难。

警钟长鸣:全部都开__int128!!!然后可以通过平方避免开根号带来的小数,然后移项,避免除法带来的小数!!
typedef struct Point{
    int x,y;
}Point;
Point point[2*100005];
Point operator-(Point a,Point b){ return {a.x-b.x,a.y-b.y}; }
__int128 operator^(Point a,Point b){
    __int128 x1=a.x,y1=a.y,x2=b.x,y2=b.y;
    return x1*y2-y1*x2;
}
__int128 cross(Point a,Point b,Point c){ return (b-a)^(c-a); }
__int128 dist(Point a,Point b){ return  (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y); }
int n;
__int128 ans=0;
int x0,yy0,r;
void rotating_calipers(){  //逆时针
    __int128 sum=0;
    for(int i=0,j=1;i<2*n;i++){
        j=max(j,i+1);
        while( j+1<2*n && cross(point[i],point[j+1],{x0,yy0})*cross(point[i],point[j+1],{x0,yy0})>=r*r*dist(point[i],point[j+1])
               && (cross(point[i],point[i+1],{x0,yy0})>0)==(cross(point[i],point[j+1],{x0,yy0})>0) ){
            ++j;
            __int128 S=cross(point[i],point[j-1],point[j]);
            sum+=S;
        }
        ans=max(ans,sum);
        sum-=cross(point[i],point[i+1],point[j]);
    }
}
//警钟长鸣:全部都开__int128!!!然后可以通过平方避免开根号带来的小数,然后移项,避免除法带来的小数!!
//Italian Cuisine
//https://codeforces.com/gym/105386
void write(__int128 x){
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
void solve(){
    cin>>n>>x0>>yy0>>r;
    for(int i=0;i<n;i++) cin>>point[i].x>>point[i].y;
    for(int i=n;i<2*n;i++) point[i]=point[i-n];
    ans=0;
    rotating_calipers();
    write(ans);
    puts("");
}

https://codeforces.com/gym/105386/problem/J--TheQuestForEI-Doradohttps://codeforces.com/gym/105386/problem/J--

思路:将近150行的长代码。思维上不算难,只是实现比较顶。还要注意各种各种细节(见代码中的wrong)。写的时候连st表的板子都敲错了。。然后还有各种细节问题。以后写这种长代码最好还是一部分一部分的功能写完就马上测试一下,不然全部堆起来哪里错了很不好找。

关键在于实现:o(logn)的复杂度在下标为[l,r]的,数字无序的区间找到第一个大于x的数字的位置,并且要维护5e5个不同这样的数组.
完全想到了是最短路,按id为第一关键字,id相等的话剩下的路程为第二关键字.
然后用st表+二分来维护.
但是问题是怎么定义这个st表?按理来说是三维的st[500005][500005][22],其中第二维是动态的,但是怎么定义?
定义一个结构体,然后用resize()...真晕了,定义出这个st数组.看了题解是用结构体和resize()定义出的.
typedef struct node1{
    int to,company,len;
}node1;
typedef struct node2{
    int id,len;
}node2;
typedef struct node3{
    int id,from,left;
    //wrong1:已经重载运算符了,不用再存负的了!!
    bool operator<(const node3 x) const{
        if(x.id!=id) return id>x.id;
        return left<x.left;
    }
}node3;
typedef struct node4{
    int id,company,left;
}node4;
int n,m,k;
vector<bool> ans(500005,false);
vector<node1> vct[500005];     //<to,company,len>
vector<node2> ticket[500005];  //<id,len>
vector<node4> dis(500005);  //<id,company,left>
//关键在于实现:o(logn)的复杂度在下标为[l,r]的,数字无序的区间找到第一个大于x的数字的位置,并且要维护5e5个不同这样的数组.
//完全想到了是最短路,按id为第一关键字,id相等的话剩下的路程为第二关键字.
//然后用st表+二分来维护.
//但是问题是怎么定义这个st表?按理来说是三维的st[500005][500005][22],其中第二维是动态的,但是怎么定义?
//定义一个结构体,然后用resize()...真晕了,定义出这个st数组.看了题解是用结构体和resize()定义出的.
typedef struct mySt{
    vector<vector<int>> f; //后两维定义在这里.
    int nn;
    void init(vector<node2> &ticket0){
        nn=(int)ticket0.size();//先给nn赋值!
        f.resize(nn+5,vector<int>(22,0));
        for(int i=0;i<nn;i++) f[i][0]=ticket0[i].len;
        for(int j=1;j<=20;j++){
            for(int i=0;i+(1ll<<j)-1<=nn-1;i++){  //wrong2:不是小于nn-1!! 得取等!!
                //wrong6!!!:一直写成了[i+(1ll<<j)]..应该是[i+(1ll<<(j-1))]
                //我曹了真的,隔一段时间没写st表,连这里都写错了,最后还是对拍出来的样例找到的错。。曹了。。
                //如果是赛时..不敢想象....写了两天晚上了,cao!
                f[i][j]=max(f[i][j-1],f[i+(1ll<<(j-1))][j-1]);
            }
        }
    }
    int query(int l,int r){
        int kk=log2(r-l+1);
        int res=max(f[l][kk],f[r-(1ll<<kk)+1][kk]);
        return res;
    }
}mySt;
mySt st[500005];
void myClear(){
    for(int i=1;i<=n;i++) {
        vct[i].clear(),ans[i]=false;
        dis[i]={INT_MAX,-1,-1};
    }
}
//The Quest for El Dorado
//https://codeforces.com/gym/105386/problem/J
void solve(){
//    cout<<5e5*(log2(5e5)+log2(5e5));
    cin>>n>>m>>k;
    myClear();
    for(int i=1;i<=m;i++){
        ticket[i].clear(); //clear
        int u,v,company,len;
        cin>>u>>v>>company>>len;
        vct[u].emplace_back(node1{v,company,len});
        vct[v].emplace_back(node1{u,company,len});
    }
    int len0=0,company0=0;
    for(int i=1;i<=k;i++){
        int company,len;
        cin>>company>>len;
        if(i==1) len0=len,company0=company;
        ticket[company].emplace_back(node2{i,len});
    }
    for(int i=1;i<=m;i++){    //初始化每个公司的st表.
        if(ticket[i].empty()) continue;
        st[i].init(ticket[i]);
    }
    priority_queue<node3> pq;   //node3<-id,from,left>
    dis[1]={1,company0,len0};
    pq.emplace(node3{1,1,len0});
    while(!pq.empty()){
        int from=pq.top().from;
        pq.pop();
        if(ans[from]) continue;
        ans[from]=true;
        int id0=dis[from].id,com0=dis[from].company,left0=dis[from].left;
        for(auto v:vct[from]){
            int to=v.to,company=v.company,len=v.len;
            if(id0==dis[to].id&&com0==company&&left0-len>=dis[to].left){
                dis[to].id=id0;
                dis[to].company=com0;
                dis[to].left=left0-len;
                pq.emplace(node3{dis[to].id,to,dis[to].left});
            }
            else if(id0<dis[to].id&&com0==company&&left0>=len){
                dis[to].id=id0;
                dis[to].company=com0;
                dis[to].left=left0-len;
                pq.emplace(node3{dis[to].id,to,dis[to].left});
            }
            else{
                int l=0,r=(int)ticket[company].size()-1,idx=-1;
                while(l<=r){       //在当前company中找到第一个大于当前id的位置
                    int mid=(l+r)>>1;
                    if(ticket[company][mid].id>id0){ //不能取等!!
                        idx=mid;
//                        finIdx=ticket[company][mid].id;//wrong3:finIdx不是在这里!!
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                if(idx==-1) continue;
                int idx2=-1,len2=-1,finIdx=-1;
                l=idx,r=st[company].nn-1;    //在当前company中找到第一个大于等于当前len的位置.
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(st[company].query(idx,mid)>=len){
                        idx2=mid;
                        finIdx=ticket[company][mid].id;//finIdx应该在这里!!
                        r=mid-1;
                    }
                    else l=mid+1;
                }
                if(idx2==-1) continue;
                len2=st[company].query(idx,idx2);
                if(finIdx<dis[to].id){
                    dis[to].id=finIdx;
                    dis[to].left=len2-len;
                    //wrong4:这里忘记更新company了!!...
                    //wrong5:这里不是com0,是company!!
                    dis[to].company=company;
                    pq.emplace(node3{dis[to].id,to,dis[to].left});
                }
                else if(finIdx==dis[to].id&&len2-len>=dis[to].left){
                    dis[to].left=len2-len;
                    //wrong4:这里忘记更新company了!!...
                    //wrong5:这里不是com0,是company!!
                    dis[to].company=company;
                    pq.emplace(node3{dis[to].id,to,dis[to].left});
                }
            }
        }
    }
    for(int i=1;i<=n;i++) ans[i]?cout<<"1":cout<<"0";
    cout<<endl;
}
//hack1: expect:11111110  output:11111000
//1
//8 9 5
//1 5 2 40
//1 2 1 30
//2 3 1 20
//2 4 5 20
//3 4 3 20
//4 8 2 30
//4 6 3 50
//6 7 3 20
//5 6 2 15
//1 49
//2 55
//3 20
//5 20
//3 20
//hack2: expect:111    output:101
//3 5 3
//2 3 2 2
//1 2 3 11
//1 2 4 15
//3 1 4 2
//3 2 3 15
//5 12
//4 7
//4 20

https://codeforces.com/gym/105386/problem/A--Two-StarContest

思路:模拟题,注意细节。。有一种情况没有留意到,导致一直wa。。见代码注释。

int n,m,k;
typedef struct node{
    int star,id;
    int sum=0;
    vector<int> v;
}node;
bool cmp1(node a,node b){
    if(a.star!=b.star) return a.star<b.star;
    return a.sum<b.sum;
}
bool cmp2(node a,node b){ return a.id<b.id; }
void solve(){
    cin>>n>>m>>k;
    vector<node> vct(n);
    for(int i=0;i<n;i++){
        cin>>vct[i].star;
        vct[i].id=i;
        for(int j=0;j<m;j++){
            int x; cin>>x;
            vct[i].v.emplace_back(x);
            if(x!=-1) vct[i].sum+=x;
        }
    }
    sort(vct.begin(),vct.end(),cmp1);
    for(int j=0;j<m;j++) if(vct[0].v[j]==-1) vct[0].v[j]=0;
    for(int i=1;i<n;i++){
        if(vct[i].sum>vct[i-1].sum||vct[i].star==vct[i-1].star&&vct[i].sum==vct[i-1].sum){
            for(int f=0;f<m;f++) {
                if(vct[i].v[f]==-1) {
                    vct[i].v[f]=0;
                }
            }
        }
            //wa:
            //排序之后有:
            //1 20
            //2 10
            //2 15  这个应该改成20,而不是21
            //2 25
        else if(vct[i].star==vct[i-1].star&&vct[i].sum<vct[i-1].sum){  这里要判断,改到相等即可!!
            int dif=vct[i-1].sum-vct[i].sum;
            for(int j=0;j<m;j++){
                if(vct[i].v[j]==-1){
                    if(dif==0) vct[i].v[j]=0;
                    else if(dif<=k) vct[i].v[j]=dif,vct[i].sum+=dif,dif=0;
                    else vct[i].v[j]=k,vct[i].sum+=k,dif-=k;
                }
            }
            if(dif!=0){ cout<<"No"<<endl; return; }
        }
        else{
            int dif=vct[i-1].sum-vct[i].sum;
            for(int j=0;j<m;j++){
                if(vct[i].v[j]==-1){
                    if(dif==0) vct[i].v[j]=1,vct[i].sum++,dif--;
                    else if(dif==-1) vct[i].v[j]=0;
                    else if(dif+1<=k) vct[i].v[j]=dif+1,vct[i].sum+=dif+1,dif=-1;
                    else vct[i].v[j]=k,vct[i].sum+=k,dif-=k;
                }
            }
            if(dif!=-1){ cout<<"No"<<endl; return; }
        }
    }
    cout<<"Yes"<<endl;
    sort(vct.begin(),vct.end(),cmp2);
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cout<<vct[i].v[j]<<" ";
        }
        cout<<endl;
    }
}

https://codeforces.com/gym/105386/problem/B--GoldMedal

思路:队友写的签到题。

void solve() {
    int n, k; cin >> n >> k;
    vector<int> a(n + 1);
    int ans = 0;
    vector<int> t;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        ans += a[i] / k;
        t.push_back((a[i] + k - 1) / k * k - a[i]);
    }
    int m; cin >> m;
    sort(t.begin(), t.end());
    for (auto i : t) {
        if (!i) continue;
        if (i > m) break;
        ans ++;
        m -= i;
    }
    cout << ans + m / k << '\n';
}

https://codeforces.com/gym/105386/problem/G--BePositive

思路:队友写的签到题。

ll n;
int ans[N];
void solve(){
	ans[1]=1,ans[2]=0;
	for(int i=3;i<=2e6+5;i++){
		if(i%4==0) ans[i]=i,ans[i+1]=i-1,i++;
		else ans[i]=i-1;
	}
	int t; cin >> t;
	while(t--){
		cin >> n;
		if(n==2){cout << "1 0\n"; continue;}
		if(n%4==0 || n==1){cout << "impossible\n"; continue;}
		for(int i=1;i<=n;i++){
			cout << ans[i] << " \n"[i==n];
//			sum^=ans[i];
//			if(!sum) if0=i;
		}
//		if(if0) cout << "\n!!!" << if0;
	}
}

https://codeforces.com/gym/105386/problem/I--LeftShifting2

思路:容易发现每次移动之和前几个和后几个有关,直接枚举即可。注意奇偶性的不同。队友写这题的时候思路宕机了,后来我给了枚举思路,队友实现了。双端队列模拟即可,注意判开头和末尾字符的奇偶性。

void solve() {
    string s; cin >> s;
    deque<int> t;
    deque<char> ts;
    for (auto i : s) ts.push_back(i);
    int cc = 1;
    for (int i = 1; i < s.size(); i ++) {
        if (s[i] == s[i - 1]) cc ++;
        else {
            t.push_back(cc);
            cc = 1;
        }
    }
    t.push_back(cc);
    string ss = s;
    int ans = 0;
    for (int i = 1; i < ss.size(); i ++) {
        if (ss[i] == ss[i - 1]) {
            ss[i] = '#';
            ans ++;
        }
    }
    int sz = s.size();
    int res = ans;
    // debug1(ans);
    for (int i = 1; i < s.size(); i ++) {
        int s0 = ts.front();
        ts.pop_front();
        int s1 = ts.front();
        ts.pop_front();
        ts.push_front(s1);
        ts.push_front(s0);
        if (s0 == s1 && t.front() % 2 == 0) ans --;
        ts.push_back(ts.front());
        ts.pop_front();
        t.front() --;
        int sn = ts.back();
        ts.pop_back();
        int sm = ts.back();
        ts.pop_back();
        ts.push_back(sm);
        ts.push_back(sn);
        if (t.front() == 0) t.pop_front();
        if (sn != sm)t.push_back(1);
        else t.back() ++;
        if (sn == sm && t.back() % 2 == 0) ans ++;

        res = min(res, ans);
    }
    cout << res << '\n';
}

https://codeforces.com/gym/105386/problem/L--Trails

思路:贴一下错误代码吧,好歹花了不少时间。。虽然说没补出来。。

int n,p,q;
pair<int,int> arr[1000006];
void write(__int128 x){
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
bool cmp(pair<int,int> a,pair<int,int> b){
    if(a.first!=b.first) return a.first<b.first;
    return a.second>b.second;
}
//题解是排序之后用什么最长上升子序列写的,不懂..
//也可以用线段树维护,很久没写过了,来一发.
//按<x,y>从小到大排序之后,用线段树维护就行,可以避免删去重复的区间--感觉都不用线段树(不会写线段树了),插入到set即可.
//一开始的想法只是直接排序之后贪心使用更左下的传送,完全不对的,会漏算答案.
//Trails
//https://codeforces.com/group/L9GOcnr1dm/contest/559336/problem/L
void solve(){
    cin>>n>>p>>q;
    swap(p,q);
    for(int i=1;i<=n;i++) cin>>arr[i].first>>arr[i].second;
    sort(arr+1,arr+n+1,cmp);
    __int128 a1=(q+1)*(1+(q+1))/2,an=a1+(p-1)*(q+1);
    __int128 ans=p*(a1+an)/2+q*(1+q)/2;
//    ans-=72+24+21+10+10;
    set<int> st;
    for(int i=1;i<=n;i++){
        if(arr[i].first>=p||arr[i].second>=q) continue;
        auto pp=st.lower_bound(arr[i].second+1);
        if(pp==st.end()) {
            ans-=(p-arr[i].first)*(q-arr[i].second);
            st.insert(arr[i].second+1);
        }
        else if(*pp==arr[i].second+1) continue;
        else{
            ans-=(p-arr[i].first)*(*pp-arr[i].second-1);
            st.insert(arr[i].second+1);
        }
    }
    write(ans);
    puts("");
}
//expect:155
//3 5 5
//1 1
//0 2
//2 3
//expect:897  output:907
//9 10 9
//1 1
//2 6
//3 3
//5 1
//5 4
//5 7
//7 3
//8 6
//9 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值