2021-2022 ACM-ICPC Latin American Regional Programming Contest 题解

B
先两边贪心,然后中间部分卷积

#pragma GCC optimize("O3")

#include<iostream>
#include<string.h>
#include<algorithm>
#include<deque>
#include<assert.h>

#define lint int64_t

#define mod1 998244353ll
#define mod2 1004535809ll
#define g 3ll
#define invg1 332748118ll
#define invg2 334845270ll

#define maxn 400005

using
        namespace
                    std;

int n;
lint a[maxn],b[maxn];
int p1,p2,sz,limit;
lint kotaemax[maxn],kotaemin[maxn];

lint qow(lint a,lint b,lint mod){
    
    lint ans=1;
    for(;b;a=a*a%mod,b>>=1) if(b&1) ans=ans*a%mod;
    return ans;
}

lint L,R[maxn];
lint A1[maxn],B1[maxn];
lint A2[maxn],B2[maxn];

void NTT(lint A[],lint mod,lint G){
    
    for(int i=0;i<limit;i++){
        
        if(i<R[i]){
            
            swap(A[i],A[R[i]]);
        }
    }
    
    for(int mid=1;mid<limit;mid<<=1){
        
        lint w1=qow(G,(mod-1)/(mid<<1),mod);
        
        for(int j=0;j<limit;j+=(mid<<1)){
            
            lint wi=1;
            
            for(int k=0;k<mid;k++,wi=wi*w1%mod){
                
                lint U=A[j+k],V=wi*A[j+k+mid]%mod;
                A[j+k]=(U+V)%mod;
                A[j+k+mid]=(U-V+mod)%mod;
            }
        }
    }
    
    if(G>3){
        
        lint invbase=qow(limit,mod-2,mod);
        
        for(int i=0;i<limit;i++){
            
            A[i]=A[i]*invbase%mod;
        }
    }
}

lint comb(lint u,lint v){
    
    /*
    ans=k1*m1+u
    ans=k2*m2+v
    k1*m1+u-v=k2*m2
    k1*m1+u-v=0 modm2
    k1=(v-u)/m1 modm2
    
    */
    
    lint k1=(v-u+mod2)%mod2*qow(mod1,mod2-2,mod2)%mod2;
    
    return k1*mod1+u;
}

deque<lint> tem;

void slv_min(){
    
    //sort(a,a+n);
    //sort(b,b+n);
    
    p1=lower_bound(a,a+n,0)-a;
    p2=lower_bound(b,b+n,0)-b;
    
    tem.clear();
    
    for(int i=0;i<min(p1,n-p2);i++){
        
        tem.push_back(a[i]*b[n-i-1]);
    }
    
    for(int i=0;i<min(n-p1,p2);i++){
        
        tem.push_back(a[n-i-1]*b[i]);
    }
    
    sort(tem.begin(),tem.end(),[&](lint u,lint v){
        return u<v;
    });
    
    lint s=0;
    
    for(int i=0;i<tem.size();i++){
        
        s+=tem[i];
        kotaemin[i+1]=s;
    }
    
    /*
    cal L, R, limit
    */
    
    sz=abs(n-p1-p2);
    
 limit=1;
 L=0;
 while(limit<=(sz<<1)){
     limit<<=1;
     L++;
 }
 for(int i=0;i<limit;i++){
     
     R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
 }
    
    memset(A1,0,limit*sizeof(lint));
    memset(A2,0,limit*sizeof(lint));
    memset(B1,0,limit*sizeof(lint));
    memset(B2,0,limit*sizeof(lint));
//  memset(A1,0,sizeof(A1));
//  memset(A2,0,sizeof(A2));
//  memset(B1,0,sizeof(B1));
//  memset(B2,0,sizeof(B2));
    
    int sta=min(p1,n-p2),stb=min(n-p1,p2);
    
    for(int i=0;i<sz;i++){
        
        A1[i]=A2[i]=abs(a[sta+i]);
        B1[i]=B2[i]=abs(b[stb+i]);
    }
    
    if(p1+p2>n){
        
        reverse(A1,A1+sz);
        reverse(A2,A2+sz);
        reverse(B1,B1+sz);
        reverse(B2,B2+sz);
    }
    
    
    
    NTT(A1,mod1,g);
    NTT(A2,mod2,g);
    NTT(B1,mod1,g);
    NTT(B2,mod2,g);
    
    for(int i=0;i<limit;i++){
        
        A1[i]=A1[i]*B1[i]%mod1;
        A2[i]=A2[i]*B2[i]%mod2;
    }
    
    NTT(A1,mod1,invg1);
    NTT(A2,mod2,invg2);
    
    for(int i=0;i<sz;i++){
        
        kotaemin[tem.size()+i+1]=s+comb(A1[i],A2[i]);
    }
}

void slv_max(){
    
    sort(a,a+n);
    sort(b,b+n);
    
    p1=lower_bound(a,a+n,0)-a;
    p2=lower_bound(b,b+n,0)-b;
    
    bool inverseA=p1>p2;
    
    if(p1>p2){
        
        swap(p1,p2);
    }
    
    tem.clear();
    
    for(int i=0;i<p1;i++){
        
        tem.push_back(a[i]*b[i]);
    }
    
    for(int i=p2;i<n;i++){
        
        tem.push_back(a[i]*b[i]);
    }
    
    sort(tem.begin(),tem.end(),[&](lint u,lint v){
        return u>v;
    });
    
    lint s=0;
    
    for(int i=0;i<tem.size();i++){
        
        s+=tem[i];
        kotaemax[i+1]=s;
    }
    
    /*
    cal L, R, limit
    */
    
    sz=p2-p1;
    
    limit=1;
    L=0;
    while(limit<=(sz<<1)){
        limit<<=1;
        L++;
    }
    for(int i=0;i<limit;i++){
        
        R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    }
    
    memset(A1,0,limit*sizeof(lint));
    memset(A2,0,limit*sizeof(lint));
    memset(B1,0,limit*sizeof(lint));
    memset(B2,0,limit*sizeof(lint));
    
//  memset(A1,0,sizeof(A1));
//  memset(A2,0,sizeof(A2));
//  memset(B1,0,sizeof(B1));
//  memset(B2,0,sizeof(B2));
    
    if(inverseA){
        
        for(int i=p1;i<p2;i++){
            
            A1[p2-i-1]=A2[p2-i-1]=abs(a[i]);
            B1[i-p1]=B2[i-p1]=abs(b[i]);
        }
    }
    else{
        
        for(int i=p1;i<p2;i++){
            
            A1[i-p1]=A2[i-p1]=abs(a[i]);
            B1[p2-i-1]=B2[p2-i-1]=abs(b[i]);
        }
    }
    
    NTT(A1,mod1,g);
    NTT(A2,mod2,g);
    NTT(B1,mod1,g);
    NTT(B2,mod2,g);
    
    for(int i=0;i<limit;i++){
        
        A1[i]=A1[i]*B1[i]%mod1;
        A2[i]=A2[i]*B2[i]%mod2;
    }
    
    NTT(A1,mod1,invg1);
    NTT(A2,mod2,invg2);
    
    for(int i=0;i<sz;i++){
        
        kotaemax[tem.size()+i+1]=s-comb(A1[i],A2[i]);
    }
}

signed main(){
    
    scanf("%lld",&n);
    
    //freopen("B.txt","w",stdout);
    
    //n=100000;
    
    for(int i=0;i<n;i++){
        
        scanf("%lld",&a[i]);
        //a[i]=i&1?0:-10000;
    }
    
    for(int i=0;i<n;i++){
        
        scanf("%lld",&b[i]);
        //b[i]=i&1?0:10000;
    }
    
    slv_max();
    
    slv_min();
    
    for(int i=1;i<=n;i++){
        
        printf("%lld %lld\n",kotaemin[i],kotaemax[i]);
    }
    
end:
    return EOF+1;
}
/*
4
-3 -4 5 -9 
-10 -6 -7 -3

-50 90
-41 118
-20 136
22 121

6
6 -1 -2 -8 -1 -9 
-10 -10 2 7 -9 -10

-63 90
-123 170
-139 212
-130 232
-120 241
-101 239

*/

F
贪心求点n-1所在连通块最多能有多少(除去点n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define mod 1000000007
#define pb push_back
typedef pair<int,int> pir;
string sstr[]={"NO\n","YES\n"};
const int N=300010;
int T,n,m;
int vis[N];
vector<int> vec[N];
void dfs(int x)
{
	vis[x]=1;
	for(auto j:vec[x]){
		if(vis[j]) continue;
		if(j==n) continue;
		dfs(j);
	}
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		vec[u].pb(v);
		vec[v].pb(u);
	}
	dfs(n-1);
	for(int i=1;i<=n;i++){
		if(vis[i]) cout<<'B';
		else cout<<'A';
	}
}

H

费用流,偶数点位置确定,考虑奇数点插入偶数点的代价,尽量小且每个位置插入1个即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f3f3f3f3f
#define mod 1000000007 
typedef pair<ll,int> pir;
string sstr[]={"No\n","Yes\n"};
const int N=200010;
int n,m,s,t,cnt=1;//从2开始,保证相反边只有低位不同 
int to[N],nxt[N],head[N],pre[N],prep[N],gnum;//pre:维护路径前置节点,prep:维护链接的边的编号 
ll wei[N],dis[N],cost[N],h[N],totf,totc;//h维护势
void add(int u,int v,ll w,ll c)
{
	to[++cnt]=v;
	wei[cnt]=w;
	nxt[cnt]=head[u];
	head[u]=cnt;
	cost[cnt]=c;
	//反向边 
	to[++cnt]=u;
	wei[cnt]=0;
	nxt[cnt]=head[v];
	head[v]=cnt;
	cost[cnt]=-c;
}
void Dijkstra()
{
	for(int i=0;i<=gnum;i++) dis[i]=inf;
	static priority_queue<pir, vector<pir>, greater<pir> > q;
	while(!q.empty()) q.pop();
	dis[s]=0;
	q.push(pir(dis[s],s));
	while(!q.empty()){
		pir tmp=q.top();
		q.pop();
		int u=tmp.second;
		if(dis[u]<tmp.first) continue;//最大费用的话改变符号 
		for(int i=head[u];i;i=nxt[i]){
			int v=to[i];
			if(wei[i]<=0) continue;//流量用完
			if(dis[v]==inf||dis[v]>dis[u]+cost[i]+h[u]-h[v]){ //最大费用的话改变符号
				dis[v]=dis[u]+cost[i]+h[u]-h[v];
				pre[v]=u;prep[v]=i;
				q.push(pir(dis[v],v));
			} 
		}
	}
	
}
pair<ll,ll> solve()
{
	while(1){
		Dijkstra();
		if(dis[t]==inf) break;
		for(int i=1;i<=gnum;i++) h[i]+=(dis[i]!=inf)? dis[i]:0;
		ll cf=inf;//当前增广路的花费和流量
		for(int i=t;i!=s;i=pre[i]) cf=min(cf,wei[prep[i]]);
		totf+=cf;
		totc+=h[t]*cf;
		assert(totc>=0);
		for(int i=t;i!=s;i=pre[i]){
			wei[prep[i]]-=cf;//正向边 
			wei[prep[i]^1]+=cf;//反向边 
		}
	}
	return pair<ll,ll>(totf,totc);
}
void init()
{
	cnt=1;
	totc=totf=0;
	for(int i=0;i<=gnum;i++) head[i]=h[i]=0;
}
int g[510][510];
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>g[i][j];
		}
	}
	s=n+3,t=n+4;
	gnum=n+5;
	init();
	for(int i=1;i<=n;i+=2) add(s,i,1,0);
	//2
	add(2,t,1,0);
	for(int i=1;i<=n;i+=2){
		if(g[i][2]!=0) add(i,2,1,g[i][2]);
	}
	for(int i=4;i<=n;i+=2){//枚举连接i,i-2的点
		add(i,t,1,0);
		for(int j=1;j<=n;j+=2){
			if(g[j][i]!=0&&g[j][i-2]!=0){
				add(j,i,1,g[j][i]+g[j][i-2]);
			}
		}
	}
	if(n%2){//奇数,最后还需要一个单独的n-1
		add(n+1,t,1,0);
		for(int j=1;j<=n;j+=2){
			if(g[j][n-1]!=0) add(j,n+1,1,g[j][n-1]);
		}
	}
	auto tmp=solve();
	cout<<tmp.second<<'\n';
}

I
模拟

#include<iostream>
#include<map>

#define maxn 200005

using
		namespace
					std;

map<string,int> s2i;
string s;
int n,st;
int a;

int check(){
	
	if(a==0){
		
		if(st==3) return 32;
		if(st==4) return 31;
		return 30;
	}
	
	int now=(1861913578+st-a)%7;
	
	switch(now){
		
		case 0:{
			
			a%=91;
			
			if(a>=60){
				
				a-=60;
				now=4;
			}
			else if(a>=30){
				
				a-=30;
				now=2;
			}
			
			break;
		}
		case 1:{
			
			if(a>=62){
				
				a-=62;
				now=0;
				
				a%=91;
				
				if(a>=60){
					
					a-=60;
					now=4;
				}
				else if(a>=30){
					
					a-=30;
					now=2;
				}
			}
			if(a>=30){
				
				a-=30;
				now=3;
			}
			
			break;
		}
		case 2:{
			
			a%=91;
			
			if(a>=61){
				
				a-=61;
				now=0;
			}
			else if(a>=30){
				
				a-=30;
				now=4;
			}
			
			break;
		}
		case 3:{
			
			if(a>=32){
				
				a-=32;
				now=0;
				
				a%=91;
				
				if(a>=60){
					
					a-=60;
					now=4;
				}
				else if(a>=30){
					
					a-=30;
					now=2;
				}
			}
			
			break;
		}
		case 4:{
			
			a%=91;
			
			if(a>=61){
				
				a-=61;
				now=2;
			}
			else if(a>=31){
				
				a-=31;
				now=0;
			}
			
			break;
		}
		default:{
			
			printf("ERROR\n");
			break;
		}
	}
	
	if(a==0){
		
		if(st==5) return 2;
		if(st==6) return 1;
		return 0;
	}
	
	int ans;
	
	if(now==3){
		
		ans=32-a;
	}
	else if(now==4){
		
		ans=31-a;
	}
	else{
		
		ans=30-a;
	}
	
	return ans;
}

signed main(){
	
	s2i["Mon"]=0;
	s2i["Tue"]=1;
	s2i["Wed"]=2;
	s2i["Thu"]=3;
	s2i["Fri"]=4;
	s2i["Sat"]=5;
	s2i["Sun"]=6;
	
	cin>>s>>n;
	st=s2i[s];
	
	int kotae=1e9;
	
	for(int i=1;i<=n;i++){
		
		cin>>a;
		
		kotae=min(kotae,check());
	}
	
	printf("%d\n",kotae);

}
/*

Thu 1
30
*/

J
只有两点全部在边界的点对才会有可能卡住。顺序遍历边界上的点,用栈判断能否匹配即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define mod 1000000007
#define pb push_back
typedef pair<int,int> pir;
string sstr[]={"NO\n","YES\n"};
const int N=200010;
int T,h,w,n,m;
vector<pir> up;
vector<pir> ri;
vector<pir> dw;
vector<pir> le;
vector<int> vec;
int stk[N];
int top=-1;
int cnt=0;
bool check(int x,int y)
{
	if(x==0||y==0||x==h||y==w) return 1;
	return 0;
}
void add(int x,int y)
{
	if(x==0) up.pb(pir(y,cnt));
	else if(x==h) dw.pb(pir(y,cnt));
	else if(y==0) le.pb(pir(x,cnt));
	else ri.pb(pir(x,cnt));
}
signed main()
{
	cin>>h>>w>>n;
	for(int i=1;i<=n;i++){
		int a1,b1,a2,b2;
		cin>>a1>>b1>>a2>>b2;
		if(check(a1,b1)&&check(a2,b2)){
			cnt++;
			add(a1,b1);
			add(a2,b2);
		}
	}
	sort(up.begin(),up.end());
	sort(dw.begin(),dw.end());
	sort(le.begin(),le.end());
	sort(ri.begin(),ri.end());
	for(auto it:up) vec.pb(it.second);
	for(auto it:ri) vec.pb(it.second);
	for(int i=dw.size()-1;i>=0;i--) vec.pb(dw[i].second);
	for(int i=le.size()-1;i>=0;i--) vec.pb(le[i].second);
	assert(vec.size()%2==0);
	for(int i=0;i<vec.size();i++){
		if(top>=0&&vec[i]==stk[top]) top--;
		else stk[++top]=vec[i];
	}
	if(top==-1) cout<<"Y\n";
	else cout<<"N\n";
}

K
签到

#include <iostream>
#include <map>
#include <vector>

std::map<char, int> mp;
std::string ss[1000006];
int main()
{
	std::ios::sync_with_stdio(false);
	// std::cin.tie(0);
	int t;
	std::cin >> t;
	std::string s;
	for (int i = 1; i<=t; ++i) {
		std::cin >> ss[i];
		mp[ss[i][0]]++;
	}
	for (int i = 1; i<=t; ++i) {
		bool flag = 1;
		for (char &j:ss[i]) {
			if (mp[j]<=0) {
				flag = 0;
			}
		}

		if (flag) {
			std::cout << "Y";return 0;
		}
	}
	std::cout << "N";



	return 0;
}

L
按照上车顺序,定义三类人的数量为 a , b , c a,b,c a,b,c
枚举第一类人上车之后的状态,只需要确定座位对上坐了2个人的对数,那么座位对上1个人的数量和0个人的的数量。钦定坐了两个人的对数为x,则有概率
p ( x ) = C n x ∗ C n − x a − 2 ∗ x ∗ 2 a − 2 ∗ x / C 2 ∗ n a p(x)=C_n^x*C_{n-x}^{a-2*x}*2^{a-2*x}/C_{2*n}^a p(x)=CnxCnxa2x2a2x/C2na
那么后面的 b , c b,c b,c类人的行为是确定的,分类讨论求贡献即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
#define inf 0x3f3f3f3f
#define mod 1000000007
#define pb push_back
ll qow(ll a,ll p) {ll ans=1;for(;p;a=a*a%mod,p>>=1) if(p&1) ans=ans*a%mod;return ans;}
ll inv(ll a) {return qow(a,mod-2);}
const int N=2000010;
int T,n,a,b,c;
int ans;
int fac[N],invfac[N];
int C(int x,int y)
{
	if(x<0||y<0||y>x) return 0;
	return fac[x]*invfac[y]%mod*invfac[x-y]%mod;
}
int cal(int x)
{
	int a1=a,b1=b,c1=c;
	int ret=C(n,x)*C(n-x,a1-2*x)%mod*qow(2,a1-2*x)%mod*inv(C(2*n,a1))%mod;//概率

	int bo=x;//两个均被占
	int si=a1-2*x;//单个座位
	int tot=a1;//贡献
	if(si>=b1){
		tot+=b1;
		bo+=b1,si-=b1;
		//剩下的是1
		if(c1<=n-si-bo) tot+=c1;
		else{
			c1-=(n-si-bo),tot+=n-si-bo;
			if(c1<=si) ;
			else{

				c1-=si;
				c1=min(c1,n-si-bo);
				tot-=c1;//原来c满意,现在不满意了
			}
		}
	}
	else{
		//先跟a匹配
		tot+=si,b1-=si,bo+=si,si=0;
		//然后自己匹配
		tot+=b1/2*2,bo+=b1/2,si=b1%2;
		if(c1<=n-si-bo) tot+=c1;
		else{
			c1-=n-si-bo,tot+=n-si-bo;
			if(c1<=si) tot+=c1;//c不满意,但是b满意了
			else{
				c1-=si;
				tot+=si;//b满意了
				c1=min(c1,n-si-bo);
				tot-=c1;
			}
		}
	}
	//cout<<"|||"<<x<<' '<<ret<<' '<<tot<<'\n';
	return ret*tot%mod;

}
signed main()
{
	fac[0]=invfac[0]=1;
	for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod,invfac[i]=inv(fac[i]);
	cin>>n>>a>>c>>b;
	if(a+b>=2*n){
		cout<<2*n<<'\n';
		return 0;
	}
	for(int i=0;i<=a/2;i++){//枚举有几个成对的
		if(i+a-2*i>n) continue;
		ans=(ans+cal(i))%mod;
	}

	int tot=0;
	for(int i=0;i<=a/2;i++){
		if(i+a-2*i>n) continue;
		tot=(tot+C(n,i)*C(n-i,a-2*i)%mod*qow(2,a-2*i)%mod*inv(C(2*n,a))%mod)%mod;
	}
	assert(tot==1);
	cout<<ans<<'\n';
}
/*
11 10 15 6

10 5 13 11

*/

M
首先,根据d排序,验证是否可能。
然后贪心,考虑将某个数i放到最前面是否可能,剩下的数仍然根据d排序,因此维护前缀每个位置还能推迟的最大值 d i − ∑ j = 1 i t j d_i-\sum_{j=1}^i t_j dij=1itj的前缀最小值验证即可。选取最小可能的值放在最前面,并暴力 更新最小值。

#include <iostream>
#include <algorithm>
#define ls (node << 1)
#define rs (node << 1|1)
using ll = long long;
// #define int ll
struct Node {
	int l, r;
	ll mn, add;
}tree[5005*4];

struct RW{
	int t, d, i;
}allrw[5005];
ll nums[5005];
int pos[5005];

int ans[5005];
bool used[5005];
ll pre_mn[5005];
signed main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int n, d;
	std::cin >>n;
	for (int i = 1; i<=n; ++i) std::cin >> allrw[i].t >> allrw[i].d, allrw[i].i = i;
	std::sort(allrw+1, allrw+1+n, [&](RW r1, RW r2) {
		return r1.d<r2.d;
	});
	for (int i = 1; i<=n; ++i) {
		nums[i] = nums[i-1]+allrw[i].t;
		pos[allrw[i].i] = i;
		if (nums[i]>allrw[i].d) {
			std::cout << "*\n";
			return 0;
		}
	}
	pre_mn[0] = 1e18;
	for (int i = 1; i<=n; ++i) {
		nums[i]=allrw[i].d-nums[i];
		pre_mn[i] = std::min(pre_mn[i-1], nums[i]);
	}
	// build(1, 1, n);
	for (int i = 1, j = 1; i<=n; ++i) {
		bool flag = 0;
		for (int k = 1; k<=n; ++k) {
			if (used[k]) continue;
			int now = pos[k];
			if (pre_mn[now-1]>=allrw[now].t) {
				flag = 1;
				ans[i] = k;
				used[k] = 1;
				nums[now] = 1e18;
				break;
			}
		}
		if (flag) {
			for (int k = 1; k<=n; ++k) {
				if (k<pos[ans[i]]) {
					if (nums[k]<1e18)
						nums[k]-= allrw[pos[ans[i]]].t;
				}
				pre_mn[k] = std::min(pre_mn[k-1], nums[k]);
			}
		}
		if (!flag) {
			while (used[allrw[j].i]) ++j;
			used[allrw[j].i] = 1;
			ans[i] = allrw[j].i;
		}
	}
	for (int i = 1; i<=n; ++i) {
		std::cout << ans[i] << " ";
	}


	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值