AtCoder Grand Contest 016题解

传送门

\(A\)

直接枚举最终的字母然后模拟就行了……

就这数据范围还是别学我写的这种做法了……

const int N=105;
char s[N];int las[26],mx[26],n,res;
int main(){
    scanf("%s",s+1),n=strlen(s+1),res=n;
    fp(i,1,n)s[i]-='a';
    fp(i,1,n)cmax(mx[s[i]],i-las[s[i]]-1),las[s[i]]=i;
    fp(i,0,25)cmax(mx[i],n-las[i]),cmin(res,mx[i]);
    printf("%d\n",res);
    return 0;
}

\(B\)

所有颜色可以被分成只出现\(1\)次的和出现多次的,而前者的\(a_i\)必定比后者小\(1\)

如果所有的\(a_i\)都相等,考虑到底是全出现\(1\)次还是全出现多次,否则必定是形如若干个\(k-1\)和\(k\),记有\(t\)个\(k-1\),那么判断一下\(t\)个只出现一次的和\(n-t\)个出现多次的是否合法就行了

const int N=1e5+5;
int a[N],n;
int main(){
    scanf("%d",&n);
    fp(i,1,n)scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    if(a[1]<a[n]-1)return puts("No"),0;
    if(a[1]==a[n])return puts(a[1]==n-1||n>=(a[1]<<1)?"Yes":"No"),0;
    R int t=1;for(;a[t]==a[t+1];++t);
    puts(a[n]>t&&n>=t+((a[n]-t)<<1)?"Yes":"No");
    return 0;
}

\(C\)

最优策略肯定是尽量让所有子矩阵的和为\(-1\)

考虑这样一种方法,在每一个\(h|i,w|j\)的\((i,j)\)位置放一个\(sk=-(k\times (hw-1)+1)\),剩下所有位置都放\(k\),那么每一个\(h\times w\)的子矩阵都会包含且仅包含一个\(sk\)和\(hw-1\)个\(k\),且总和为\(-1\)

同时我们还要保证整个矩阵的和为正数,设\(a=n/h,b=m/w\)(都是下取整),那么\([1,1]\)到\([ah,bw]\)的这个子矩阵的和必定是\(-ab\),而剩下的部分因为都是填\(k\),所以只有让\(k\)尽量大才有可能让整个矩阵的和为正数,那么取能取的最大的\(k\)就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=505,L=1e9;
int mp[N][N],n,m,h,w,k;ll sum,sk;
int main(){
    scanf("%d%d%d%d",&n,&m,&h,&w);
    if(h==1&&w==1)return puts("No"),0;
    k=(L-1)/(h*w-1),sum=1ll*k*(n*m-(n/h*h)*(m/w*w));
    sk=1ll*k*(h*w-1)+1;
    if((n/h)*(m/w)>sum)return puts("No"),0;
    puts("Yes");
    for(R int i=h;i<=n;i+=h)for(R int j=w;j<=m;j+=w)mp[i][j]=1;
    fp(i,1,n){
        fp(j,1,m)printf("%d ",mp[i][j]?-sk:k);
        puts("");
    }
    return 0;
}

\(D\)

并没有想到用权值建点……

首先我们把\(a_{n+1}\)设为所有数的异或和,那么一次操作等价于交换\(a_{n+1}\)和某个数

对于每一个\(i\),如果\(a_i=b_i\)显然不用进行操作,否则我们把它们对应的权值连一条边,这样图里会形成若干个连通块,我们发现每条边都要有一次贡献,而且跨越连通块的时候也会有一次贡献,所以答案就是边数加上联通块个数减一

不过有可能一开始最后一个元素并没有边连出去,那么他要进入某个连通块才能继续,所以这种情况下答案要\(+1\)

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=2e5+5;
int a[N],b[N],c[N],d[N],fa[N],n,tot,res;map<int,int>mp;
inline int find(R int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline int ID(R int x){return mp[x]?mp[x]:mp[x]=++tot;}
int main(){
    scanf("%d",&n);
    fp(i,1,n)scanf("%d",&a[i]),a[n+1]^=a[i];
    fp(i,1,n)scanf("%d",&b[i]),b[n+1]^=b[i];
    fp(i,1,n+1)c[i]=a[i],d[i]=b[i];
    sort(c+1,c+2+n),sort(d+1,d+2+n);
    fp(i,1,n+1)if(c[i]!=d[i])return puts("-1"),0;
    fp(i,1,n+1)fa[i]=i;
    fp(i,1,n)if(a[i]!=b[i]){
        R int u=ID(a[i]),v=ID(b[i]);
        fa[find(u)]=find(v),++res;
    }
//  puts("qwq");
    if(!res)return puts("0"),0;
    fp(i,1,tot)if(find(i)==i)++res;
    bool fl=1;
    fp(i,1,n)if(b[i]==a[n+1])fl=0;
    printf("%d\n",res+fl-1);
    return 0;
}

\(E\)

好秒的题目……一开始口胡了一个\(O(nm\log n)\)的做法结果发现题目看错了……

我们先考虑一个更一般的问题,即定义\(f_{S,r}\)表示在\(r\)步之后集合\(S\)中的元素是否可以全部存在,是的话记为\(1\)否则为\(0\)

假设第\(r\)步要吃的鸡是\(u,v\),那么分情况讨论

  • 如果\(u\in S\)且\(v\in S\),则\(f_{S,r}=0\)
  • 如果\(u\in S\)且\(v\notin S\),则\(f_{S,r}=f_{S\cup\{v\},r-1}\)
  • 如果\(v\in S\)且\(u\notin S\),同上
  • 否则\(f_{S,r}=1\)

对于每一个点\(u\),初始时令\(S=\{u\}\),然后我们倒着考虑,不断往\(S\)中加数,如果有一次不合法就不合法,最终可以判断\(u\)是否有可能不被吃

然后是结论,\(u,v\)可以同时存在当且仅当\(u,v\)都有可能不被吃且\(S_u\cap S_v=\varnothing\)

前一个条件显然,对于后面那个条件,假设存在一个点\(w\),满足\(w\in S_u\)且\(w\in S_v\),那么如果\(w\)在两个集合中用来救了两只不同的鸡,显然\(u,v\)不可能同时活着了。如果\(w\)用来救了同一只鸡,那么我们删掉\(w\),此时必定存在另一个点\(p\)也同时在两个集合中,继续递归考虑就行了。鉴于最后两个集合分别是\(\{u\}\)和\(\{v\}\),所以一定会有终止的时候

复杂度为\(O(nm+n^3)\)

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5;
bool ins[405][405],vis[405];
int fr[N],to[N],n,m,res;
int main(){
    scanf("%d%d",&n,&m);
    fp(i,1,m)scanf("%d%d",&fr[i],&to[i]);
    fp(i,1,n){
        ins[i][i]=1;
        fd(j,m,1){
            R int u=fr[j],v=to[j];
            if(ins[i][u]&&ins[i][v]){vis[i]=1;break;}
            if(ins[i][u]||ins[i][v])ins[i][u]=ins[i][v]=1;
        }
    }
    fp(i,1,n)if(!vis[i])fp(j,i+1,n)if(!vis[j]){
        R int fl=1;
        fp(k,1,n)if(ins[i][k]&&ins[j][k]){fl=0;break;}
        res+=fl;
    }
    printf("%d\n",res);
    return 0;
}

\(F\)

首先先手必赢当且仅当\(1,2\)点的\(SG\)值异或和不为\(0\),因为这个东西很难算,我们考虑计算\(1,2\)的\(SG\)值异或为\(0\)的方案数

对于\(U\),我们设\(f[U]\)表示点集\(U\)内部使得\(1,2\)的\(SG\)值相等的连边方案数,计算的话,我们枚举\(S\),令\(T=U-S\),假设\(S\)中所有数的\(SG\)值为\(0\)(即必败态),\(T\)中所有数的\(SG\)值不为\(0\)

首先\(S\)内部肯定不能连边(必败态不能转移到必败态),而\(T\)中每一个点至少得有一条到\(S\)中的边(必胜态至少有一个后继是必败态),\(S\)中的任何一个点到\(T\)随便连边(必败态的后继全是是必胜态),最后连边方案乘上\(f[T]\)就是对应的贡献了

注意,\(1,2\)必须得同时在\(S\)或\(T\)中否则不合法

为什么这样转移是对的呢?因为我们的\(dp\)等价于把\(U\)分成了若干层,其中每一层的\(SG\)值都是一样的,而我们转移的时候可以保证\(1,2\)在同一层,所以就没有问题了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=1e9+7;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=25,M=(1<<15)+5;
int to[N],sz[M],lb[M],f[M],bin[M],n,m,lim;
int main(){
    scanf("%d%d",&n,&m),lim=(1<<n);
    for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),--u,--v,to[u]|=1<<v;
    bin[0]=1;fp(i,1,m)bin[i]=mul(bin[i-1],2);
    fp(i,1,lim-1)sz[i]=sz[i>>1]+(i&1),lb[i]=i&1?0:lb[i>>1]+1;
    f[0]=1;
    fp(u,1,lim-1)if((u&1)==(u>>1&1))
        for(R int s=u;s;s=(s-1)&u)if((s&1)==(s>>1&1)){
            R int t=u^s,res=f[t];
            for(R int i=s,j=lb[i];i;i-=i&-i,j=lb[i])
                res=mul(res,bin[sz[to[j]&t]]);
            for(R int i=t,j=lb[i];i;i-=i&-i,j=lb[i])
                res=mul(res,bin[sz[to[j]&s]]-1);
            upd(f[u],res);
        }
    printf("%d\n",dec(bin[m],f[lim-1]));
    return 0;
}

原文地址:https://www.cnblogs.com/yuanquming/p/11573074.html

时间: 2024-11-09 11:32:07

AtCoder Grand Contest 016题解的相关文章

AtCoder Grand Contest 016

AtCoder Grand Contest 016 A - Shrinking 你可以进行一个串的变换,把一个长度为\(n\)的串\(S\)可以变成长度为\(n-1\)的串\(T\),其中\(T_i\)要么是\(S_i\)要么是\(S_{i+1}\). 现在问你最少进行多少次这个操作,能够使最终得到的\(T\)只由一个字符构成. \(|S|\le 100\) 首先枚举最终字符是哪一个.那么首先在\(S\)末尾加上一个这个字符,那么这个最小步数等于对于所有位置而言,离它最近的枚举的字符到这个位置的

AtCoder Grand Contest 016 C - +/- Rectangle

题目传送门:https://agc016.contest.atcoder.jp/tasks/agc016_c 题目大意: 给定整数\(H,W,h,w\),你需要判断是否存在满足如下条件的矩阵,如果存在,则输出任意一种可能的方案 矩阵是\(H\)行\(W\)列 矩阵的每个元素的权值在\([-10^9,10^9]\)之间 矩阵的所有元素权值和为正 任意大小为\(h×w\)的子矩阵的元素权值和为负 构造题一般都是智商题--首先\(\%\%\%wzy\) 既然子矩阵的值是负数,那么我们就尽可能让它负的少

AtCoder Grand Contest 016 E - Poor Turkeys

题目传送门:https://agc016.contest.atcoder.jp/tasks/agc016_e 题目大意: 有\(N\)只火鸡,现有\(M\)个人,每个人指定了两只火鸡\(x,y\),每人依次进行操作,会从\(x,y\)中选一只火鸡吃掉:如果只有一个,那么必定吃掉剩下那个:如果都没有,这个人只能饿着肚子离开了-- 问最后有多少对火鸡可能存活 考虑倒序,我们设状态\(f_{i,j}\)表示如果要留下\(i\),那么是否要炖了\(j\),初始状态\(f_{i,i}=1\) 我们倒序考虑

AtCoder Grand Contest 007题解

传送门 \(A\) 咕咕咕 //quming #include<bits/stdc++.h> #define R register #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i) #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i) #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v) template<

AtCoder Grand Contest 008题解

传送门 \(A\) 分类讨论就行了 然而我竟然有一种讨论不动的感觉 int x,y; inline int min(R int x,R int y){return x<y?x:y;} inline int min(R int a,R int b,R int c,R int d){ return min(min(a,b),min(c,d)); } inline int calc(R int x,R int y){return y>=x?y-x:x-y+2;} int main(){ scanf(

AtCoder Grand Contest 014题解

传送门 \(A\) 首先大力猜测一下答案不会很大,所以次数大于\(10^6\)输出\(-1\)就行了 不过我并不会证上界,据说是因为如果\(a=b=c\)且都是偶数肯定\(-1\),否则设\(a\leq b\leq c\),则最大最小值的差为\(c-a\),一次操作之后变成了\({c-a\over 2}\),所以操作次数就是\(\log\)级别的了 typedef long long ll; ll a,b,c,sum;int res; int main(){ scanf("%lld%lld%ll

AtCoder Grand Contest 039 题解

传送门 \(A\) 首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下 //quming #include<bits/stdc++.h> #define R register #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i) #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i) #define go(u) for(int i=head[u],v=

Atcoder Grand Contest 003 题解

A - Wanna go back home 如果有S就必须要有N,反之亦然,如果有E必须要有W,反之亦然.判断一下就好了. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int

AtCoder Grand Contest 008 题解

A - Simple Calculator 模拟+分类讨论即可. //waz #include <bits/stdc++.h> using namespace std; #define mp make_pair #define pb push_back #define fi first #define se second #define ALL(x) (x).begin(), (x).end() #define SZ(x) ((int)((x).size())) typedef pair<