• AGC002 补题小结


    Problem A Range Product

    简要题意:给定 (a,b) 两个数字,输出 (prod_{i=a}^b i) 是正数,负数还是零。 (-10^9 le ale ble 10^9)

    tag:分类讨论

    题解:分类讨论一下没了

    #include <cstdio>
    int main (){
        int a,b;scanf ("%d%d",&a,&b);
        if (a<=0&&b>=0) puts("Zero");
        else if (a<0&&b<0&&((b-a+1)&1)) puts("Negative");
        else puts("Positive");
        return 0;
    }
    

    Problem B Box and Ball

    简要题意:(N) 个盒子,第一个盒子里是个红球,其他 (N-1) 个盒子里都是白球。现在给出 (M) 次操作,操作的方式是在一个盒子中随机取出一个球放入另一个盒子中。在 (M) 次操作完后,求出红球可能在的盒子的总数。(N,M le 10^5)

    tag:枚举,按题意模拟

    题解:按题意模拟,记录每个盒子是否可能有球

    #include <cstdio>
    const int N=100005;
    bool vis[N];int cnt[N];
    int main (){
        int n,m;scanf ("%d%d",&n,&m);vis[1]=true;
        for (int i=1;i<=n;i++) cnt[i]=1;
        for (int i=1,x,y;i<=m;i++){
            scanf ("%d%d",&x,&y);
            vis[y]|=vis[x],cnt[x]--,cnt[y]++;
            if (cnt[x]==0) vis[x]=false;
        }
        int ans=0;for (int i=1;i<=n;i++) ans+=vis[i];
        printf ("%d",ans);
        return 0;
    }
    

    Problem C Knot Puzzle

    简要题意:有 (N) 条绳子,第 (i) 条的长度是 (A_i) 。 相邻两条绳子打了结,每次选一段绳子, 如果这段绳子的总长度大于给定的变量 (L) ,我们可以在这段绳子中选一个解开。问是否有一种方案把所有绳子都解开。 (N le 10^5)

    tag:贪心

    题解:考虑进行到只剩一个绳结,则两段绳子的长度和必须大于等于L,否则这个绳结没法解。所以找到整根绳子中长度和大于等于L的两段,将左边的绳结从左往右解开,右边的绳结从右往左解开,最后将这个绳结解开。

    #include <cstdio>
    const int N=100005;
    int a[N];
    int main (){
        int n,L;scanf ("%d%d",&n,&L);
        for (int i=1;i<=n;i++) scanf ("%d",&a[i]);
        for (int i=1;i<n;i++){
            if (a[i]+a[i+1]>=L) {
                puts("Possible");
                for (int j=1;j<i;j++)  printf ("%d
    ",j);
                for (int j=n-1;j>i;j--) printf ("%d
    ",j);
                printf ("%d",i);
                return 0;
            }
        }
        puts("Impossible");
        return 0;
    }
    

    Problem D Stamp Rally

    简要题意:一张连通图, (q) 次询问从两个点 (x)(y) 出发,希望经过的点(不重复)数量等于 (z) ,经过的边最大编号最小是多少。(n,q le 10^5)

    tag:整体二分,带撤销并查集

    题解:(Atcoder) 里罕见的数据结构题,对所有询问一起二分答案,用并查集判断是否可行,挺套路的一种题。

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N=1e5+5;
    int ans[N];
    inline int Max(int a,int b){
    	return a>b?a:b;
    }
    struct Edge{
    	int u,v;
    }e[N];
    struct Node{int id,x,y,z;};
    int fa[N];
    inline int getrt(int x){while (fa[x]!=x)x=fa[x];return x;}
    int size[N],deep[N],s[N][2],top=0;
    bool tag[N];
    inline void link(int x,int y){
    	if (deep[x]<deep[y]) swap(x,y);
    	++top,s[top][0]=x,s[top][1]=y;
    	fa[y]=x,size[x]+=size[y];
    	if (deep[x]==deep[y]) deep[x]++,tag[top]=1;else tag[top]=0;
    }
    inline void pop(){
    	fa[s[top][1]]=s[top][1],size[s[top][0]]-=size[s[top][1]];
    	deep[s[top][0]]-=tag[top];tag[top]=0;--top;
    }
    inline void work(int l,int r,vector <Node > now){
    	if (now.size()==0) return;
    	if (l==r){
    		for (int i=0;i<now.size();i++) ans[now[i].id]=l;
    		return;
    	}
    	vector <Node > mx,mn;mx.clear(),mn.clear();
    	int mid=(l+r)>>1,nowtop=top;
    	for (int i=l,x,y;i<=mid;i++) if ((x=getrt(e[i].u))!=(y=getrt(e[i].v))) link(x,y);
    	for (int i=0;i<now.size();i++){
    		int x=getrt(now[i].x),y=getrt(now[i].y),sum=0;
    		if (x!=y) sum=size[x]+size[y];
    		else sum=size[x];
    		if (sum>=now[i].z) mn.push_back(now[i]);
    		else mx.push_back(now[i]);
    	}
    	work(mid+1,r,mx);
    	while (top>nowtop) pop();
    	work(l,mid,mn);
    }
    int main (){
    	int n,m,q;scanf ("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    		scanf ("%d%d",&e[i].u,&e[i].v);
    	scanf ("%d",&q);
    	vector <Node > t;t.clear();
    	for (int i=1;i<=q;i++){
    		int x,y,z;scanf ("%d%d%d",&x,&y,&z);
    		t.push_back((Node){i,x,y,z});
    	}
    	for (int i=1;i<=n;i++) fa[i]=i,size[i]=deep[i]=1;
    	work(1,m,t);
    	for (int i=1;i<=q;i++) printf ("%d
    ",ans[i]);
    	return 0;
    }
    
    

    Problem E Candy Piles

    简要题意:桌子上有 (n) 堆糖,第 (i) 堆有 (a_i) 个,两个人轮流玩游戏,有两种操作:

    1. 把最多的一堆吃完
    2. 把每一堆吃掉一个

    吃完的人输,问两人足够聪明的博弈结果。 (nle 10^5,a_ile 10^9)

    tag:博弈,推结论

    题解:首先转化为平面上,按照 (a_i) 从大到小排序,两个人轮流走,可以向上或者向右走,走不了的人失败

    有个结论,((x,y)) 这个状态如果对于先手是必胜态,那么 ((x+1,y+1)) 也对于先手是必胜态,反之亦然

    所以我们可以直接找到 ((0,0)) 对应的是哪个点 ((i,i)) ,算出这个点到上方和右方轮廓的距离,只要有一个是偶数,就先手必胜。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N=100005;
    int a[N];
    bool cmp(int a,int b){return a>b;}
    int main (){
    	int n;scanf ("%d",&n);
    	for (int i=1;i<=n;i++) scanf ("%d",&a[i]);
    	sort(a+1,a+n+1,cmp);
    	for (int i=1;i<=n;i++)
    		if (i+1>a[i+1]){
    			int j=i+1;while (a[j]==i) j++;
    			if (((j-i-1)&1)|((a[i]-i)&1)) puts("First");
    			else puts("Second");
    			return 0;
    		}
    	return 0;
    }
    

    Problem F Leftmost Ball

    简要题意:给你 (n) 种颜色的球,每个球有 (k) 个,把这 (n imes k) 个球排成一排,把每一种颜色的最左边出现的球涂成白色(初始球不包含白色),求有多少种不同的颜色序列,答案对 (10^9+7) 取模 。(nle 2000)

    tag:计数类 (dp) ,组合数学

    题解:设 (f[i][j]) 表示填了 (i) 个白色球,(j) 彩色球的方案数,那么显然 (jle i)

    考虑这个转移,首先可以填一个白色,就是 (f[i][j]+=f[i-1][j])

    放新的颜色的球:即从 (f[i][j-1]) 转移,先在剩下 (n-j+1) 种没有放置过的颜色中选择一个作为这次放置的颜色,放置在第一个空位(按转移规则,已经放置的 (i) 个白球一定都在当前第一个空位的前面,因此必定合法)。放完后还剩 (k-2) 的这个颜色的球没有放,显然只需要在剩下的后面 (n imes k-i-(j-1) imes (k-1)-1) 个空位里找任意 (k-2) 个空位放就好,有 $ inom{n imes k−(i+(j−1) imes (k−1))}{k−2}$ 种方案

    所以总的转移方程是

    [f[i][j]=f[i-1][j]+f[i][j-1] imes (n-j+1) imes inom{n imes k−(i+(j−1) imes (k−1))}{k−2} ]

    注意特判 (k=1) 的情况

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N=2005,Mod=1e9+7;
    int dp[N][N],fac[N*N],inv[N*N];
    inline int C(int n,int m){
    	return 1ll*fac[n]*inv[m]%Mod*inv[n-m]%Mod;
    }
    inline int qpow(int a,int b){
    	int ans=1;
    	while (b){
    		if (b&1) ans=1ll*ans*a%Mod;
    		a=1ll*a*a%Mod,b>>=1;
    	}
    	return ans;
    }
    int main (){
    	int n,k;scanf ("%d%d",&n,&k);
    	if (k==1) {puts("1");return 0;}
    	dp[0][0]=1;fac[0]=1;
    	for (int i=1;i<=n*k;i++) fac[i]=1ll*fac[i-1]*i%Mod;
    	inv[n*k]=qpow(fac[n*k],Mod-2);for (int i=n*k-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%Mod;
        for (int i=0;i<=n;i++) dp[i][0]=1;
    	for (int i=1;i<=n;i++)
            for (int j=0;j<=i;j++)
                dp[i][j]=dp[i-1][j]+1ll*dp[i][j-1]*(n-j+1)%Mod*C(n*k-(i+(j-1)*(k-1))-1,k-2)%Mod,dp[i][j]%=Mod;
        printf ("%d",dp[n][n]);
    	return 0;
    }
    
  • 相关阅读:
    tcpdump 命令的常用选项:一
    Centos系统中 Systemd 的Unit文件配置说明
    如何使用PowerShell获取物理磁盘的信息
    tcpdump 命令的常用选项:二
    Google报告:大量被入侵的 Google Cloud 实例被用来挖掘加密货币
    Ubuntu中使用pdftk合并、分离PDF文档等操作
    tcpdump 命令的常用选项:三
    优麒麟Ubuntu Kylin 20.04 Pro SP1 上线
    为SSH登录设置电子邮件提醒
    图片上传并显示(兼容ie),图片大小判断
  • 原文地址:https://www.cnblogs.com/crazyzh/p/10883753.html
Copyright © 2020-2023  润新知