B题

题意:现已给出长度为n的数组a,表示n张已确定值的牌(1<=ai<=m)。还有k张没有确定值的鬼牌,每张可为[1,m]内任意一个值。问从这n+k张牌中选出x张数值连续的牌,求x能达到的最大值。

———————————————————————————————————

思路:双指针维护窗口,窗口内的数值是可在使用不超过k张鬼牌的情况下连续的数。

用队列来实现双指针不容易出现边界错误且代码更短逻辑更清晰

时间复杂度:排序O(nlogn)+双指针O(n)

void solved()
{
    int n,m,k;
	cin>>n>>m>>k;
    
    vector<int> a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];
    
    sort(a.begin()+1,a.end());//排序
    n=unique(a.begin()+1,a.end())-a.begin()-1;//去重
    
    queue<int> q;//队列 
    int ans=0,cost=0;//cost是当前消耗的鬼牌
    for(int i=1;i<=n;i++)
    {
    	while(q.size() && a[i]-q.back()-1+cost>k)//确保a[i]能加入队列
    	{
    		int x=q.front();q.pop();
    		if(q.size())cost-=q.front()-x-1;//取消连接时撤回前面消耗的鬼牌数
		}
		
		//加入a[i] 
		if(q.size())cost+=a[i]-q.back()-1;//与前面连接新消耗的鬼牌 
		q.push(a[i]);
		
		//计算答案 
		int l=q.front(),r=q.back();
		ans=max(ans,r-l+1+min((l-1)+(m-r),k-cost));//k-cost张剩下的鬼牌用来向左向右补长[l,r]的区间 
	}
	cout<<ans<<'\n';
}

C题

需要求组合数,先O(n)预处理出1e7范围内的阶乘及其阶乘逆元,然后处理每一个询问即可

正面考虑所有美丽图的情况太多,所以考虑所有情况减去不是美丽图的情况

注意不是美丽图的情况:C(n/2,k)*(2^k) (其中k<=n/2)

解释:C(n/2,k)是因为n/2对直径中任意选k对,(2^k)是因为每对直径只能涂一个点,所以每队用2种涂法,k对就是2^k种,然后根据乘法原理乘起来即可

void solved()
{
    auto qpow=[&](int a,int b)//快速幂
    {
        int res=1;
        while(b)
        {
            if(b&1)res=res*a%mod;
            a=a*a%mod;
            b/=2;
        }
        return res;
    };
    
    int n,k;
    n=10000000;
    vector<int> f(n+1),ff(n+1);
    
    f[0]=1;
    for(int i=1;i<=n;i++)f[i]=f[i-1]*i%mod;//阶乘
    ff[n]=qpow(f[n],mod-2);
    for(int i=n-1;i>=0;i--)ff[i]=ff[i+1]*(i+1)%mod;//阶乘逆元
    
    auto fc=[&](int n,int m)//组合数
    {
        return f[n]*ff[n-m]%mod*ff[m]%mod;
    };
    
    int t;cin>>t;
    while(t--)
    {
        cin>>n>>k;
        
        if(k<2 || n%2==1)cout<<0<<'\n';//不可能是美丽图
        else
        {
    		if(k>n/2)cout<<fc(n,k)<<'\n';//任意情况都是美丽图
    		else cout<<(fc(n,k)-qpow(2,k)*fc(n/2,k)%mod+mod)%mod<<'\n';//考虑所有情况减去不是美丽图的情况
		}
    }
}