文章目录
- 前言
- CF104A Blackjack
- CF103A Testing Pants for Sadness
- CF103B Cthulhu
- CF103C Russian Roulette
- CF103D Time to Raid Cowavans
- CF103E Buying Sets
前言
比较水的远古比赛
104A、103A、103B是水题
103C是兼有细节处理和思维含量
103D是小清新简单分块题
103E是神仙网络流(强烈推荐)
CF104A Blackjack
Description\text{Description}Description
Blackjack 是一个扑克牌游戏。
Blackjack 使用除了两张王以外的全部 52 张卡牌,也就是 2,3,4,5,6,7,8,9,10,J,Q,K,A2,3,4,5,6,7,8,9,10,J,Q,K,A2,3,4,5,6,7,8,9,10,J,Q,K,A。其中规定 2,3,4,5,6,7,8,9,102,3,4,5,6,7,8,9,102,3,4,5,6,7,8,9,10 的点数为 2,3,4,5,6,7,8,9,102,3,4,5,6,7,8,9,102,3,4,5,6,7,8,9,10,J,Q,KJ,Q,KJ,Q,K 的点数均为 101010,AAA 的点数同时为 111 或 111111,这取决于玩家的意愿。虽然扑克牌有花色,但是一张卡牌的点数与其花色无关。这个游戏的规则很简单:拿两张牌,如果这两张牌的点数之和等于 nnn,玩家就赢了,否则玩家就输了。
现在玩家已经拿了一张黑桃 QQQ,求在其他牌中再抽一张,能使玩家赢得游戏的方案数。
Solution\text{Solution}Solution
垃圾水题。
分情况特判一下即可。
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,k,m;signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endif//debug("sdsa");n=read()-10;if(n<=0) printf("0");else if(n<=9) printf("4");else if(n==10) printf("15");else if(n==11) printf("4");else printf("0");return 0;
}
/*
*/
CF103A Testing Pants for Sadness
Description\text{Description}Description
有个人要做 nnn 道选择题,必须按 1∼n1\sim n1∼n 的顺序答题,第 iii 题有 aia_iai 个选项。不幸的是,这些题这个人一道也不会,只能猜选项,但是他的记忆非常好,可以记住所有题曾经的正确选项。当他做错一道题时,他就必须从 111 重新开始选,假设题目的正确选项不会变,在最坏的情况下,若要做对所有题,他一共选了多少次选项?
1≤n≤100,1≤ai≤1091\leq n\leq 100,1\leq a_i \leq 10^91≤n≤100,1≤ai≤109
Solution\text{Solution}Solution
个人心中签到题的榜样。
没码量,又有一点点思维。
对于第 iii 关单独考虑考虑。
首先,每个错误选项都会且只会选一次,也就是 1×(ai−1)1\times(a_i-1)1×(ai−1)。
对于正确选项,它会被选做到这一关的次数,换句话说就是之后答错的次数 +1+1+1,也就是 ∑j=i+1n(aj−1)+1\sum_{j=i+1}^n(a_j-1) +1∑j=i+1n(aj−1)+1
全加起来即可,后缀和优化一下,时间复杂度 O(n)O(n)O(n)。
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n,k,m;
ll a[N],suf[N],ans;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();for(int i=1;i<=n;i++) a[i]=read();suf[n+1]=1;for(int i=n;i>=1;i--) suf[i]=suf[i+1]+a[i]-1;for(int i=1;i<=n;i++) ans+=a[i]-1+suf[i+1];printf("%lld\n",ans);return 0;
}
/*
*/
CF103B Cthulhu
Description\text{Description}Description
给出一个图,判断它是不是一个环的大小不小于 333 的奇环树。
Solution\text{Solution}Solution
这题的唯一难度可能仅在于耐着性子把英语题面看完…
看懂题面后就几乎没有难度了。
dfs 一遍即可。
别忘了判连通性。
(后来看题解才想起来本题根本就没有重边,所以根本不需要 dfs,并查集判一下连通性就可以了。)
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while (isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f;
}int n,m;
int flag;
int vis[N];
vector<int>v[N];
void dfs(int x,int fa){vis[x]=1;for(int to:v[x]){if(to==fa) continue;if(vis[to]) flag=1;else dfs(to,x);}return;
}
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();m=read();if(m!=n){printf("NO");return 0;}for(int i=1;i<=m;i++){int x=read(),y=read();v[x].push_back(y);v[y].push_back(x);}dfs(1,0);if(!flag) printf("NO");else{for(int i=1;i<=n;i++){if(!vis[i]){printf("NO");return 0;}}printf("FHTAGN!");}return 0;
}
/*
*/
CF103C Russian Roulette
Description\text{Description}Description
两个傻瓜玩俄罗斯轮盘(轮流开枪,直到一方死亡),nnn 个位置里有 kkk 个子弹,请你填装这 kkk 个子弹,使先开枪者死亡的概率最小,在满足该条件的情况下最小化方案的字典序(空弹夹字典序更小)。
每次询问位置 xxx 是否装有子弹。
k≤n≤1018k\le n\le10^{18}k≤n≤1018
Solution\text{Solution}Solution
有点小细节的一道题。
(样例真心良心)
有子弹的地方先手必然死,所以我们就是让空弹夹先手死的尽可能少就行了。
通过观察样例二可以发现,从后往前,隔一个放一颗子弹是一种很好的方案。
但是观察样例三可以发现,在刚才那种构造的基础上,有的时候可以把一枚最前面的子弹挪到后面挨着放,死亡概率不变,但字典序更小。
具体的,这种情况是在前面空出的连续空白段长度为偶数时成立,这时候前面去掉一个必胜位置的数量不变。
还有一些其它边边角角的情况,在 2k≥n2k\ge n2k≥n 时,后面挨着放前面隔着放即可;还要注意 n=1n=1n=1 的时候不能“把最前面的一个挪到后面挨着放”(因为一共就只有一个)
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while (isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f;
}ll n,k,m;
int op;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();k=read();m=read();ll pl(0);if(k*2>=n){op=1;pl=2*(n-k);}else if((n-2*k)%2==0||k<=1){op=2;pl=n-2*k;}else{op=3;pl=2*k-2;}for(int i=1;i<=m;i++){ll x=read();if(op==1){if(x>pl||(x&1)==0) printf("X");else printf(".");}else if(op==2){if(x<=pl||((x-pl)&1)) printf(".");else printf("X");}else{x=n-x+1;if(x<=2||(x<=pl&&x%2==0)) printf("X");else printf(".");}}return 0;
}
/*
*/
CF103D Time to Raid Cowavans
Description\text{Description}Description
一个序列 aaa ,mmm 次询问,每次询问给出 t,kt, kt,k。求 at+at+k+at+2k+⋯+at+pka_t + a_{t+k}+a_{t+2k}+\cdots+a_{t+pk}at+at+k+at+2k+⋯+at+pk 其中 t+pk≤nt+pk \leq nt+pk≤n 且 t+(p+1)k>nt+(p+1)k > nt+(p+1)k>n。
n,m≤300000,ai≤109n,m \leq 300000,a_i \leq 10^9n,m≤300000,ai≤109
Solution\text{Solution}Solution
似乎题解有对前缀和进行分块优化空间从而在线的神仙做法?
但是这题离线就挺香的了
不难想到根号分治,对于 k>nk>\sqrt nk>n 的询问,直接暴力即可。
对于 k≤nk\le \sqrt nk≤n 的询问,离线下来对于 n\sqrt nn 个可能的 kkk 分别处理前缀和就可以 O(1)O(1)O(1) 回答所有询问。
时间复杂度 O(nn)O(n\sqrt n)O(nn),空间复杂度 O(n)O(n)O(n)。
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=3e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while (isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f;
}int n,m;ll sum[N],a[N];
struct query{int st,d,id;bool operator < (const query o)const{return d<o.d;}
}q[N];
int tot;
ll ans[N];
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();int w=sqrt(n);for(int i=1;i<=n;i++) a[i]=read();m=read();for(int i=1;i<=m;i++){int aa=read(),b=read();if(b>w){ll res(0);for(int j=aa;j<=n;j+=b) res+=a[j];ans[i]=res;}else q[++tot]=(query){aa,b,i};}sort(q+1,q+1+tot);int now(0);for(int i=1;i<=tot;i++){if(now!=q[i].d){now=q[i].d;for(int j=1;j<=n;j++){sum[j]=a[j];if(j>now) sum[j]+=sum[j-now];} }int id=q[i].id,st=q[i].st,d=q[i].d,ed=st+(n-st)/d*d;ans[id]=sum[ed];if(st>now) ans[id]-=sum[st-now];}for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);return 0;
}
/*
*/
CF103E Buying Sets
Description\text{Description}Description
有一个大小为 nnn 的全集,每个元素是一个数,有 nnn 个子集。题目保证任意 kkk 个子集的并的大小 ⩾k\geqslant k⩾k 。
每个子集有一个可正可负的权值,你需要选出一些子集使得这些子集并的大小等于子集个数,且所选子集的权值和最小。可以为空集。
n≤300n\le 300n≤300
Solution\text{Solution}Solution
一道网络流比较神的题。
首先可以补集转化,选的权值最小,转化为不选的权值最大。
然后由于这种题大多数都是转成最小割,所以把所有权值取反。
然后就是关键的建图:
原点向所有集合连容量等于集合权值 INFINFINF 的边,集合向包含的数字连 INFINFINF 的边,数字向汇点连 INFINFINF 的边。
显然,断数字和集合之间的边是不优的;而其中集合断边相当于不选,数字断边相当于选。
这样,考虑任意一种割的方案,都说明选择了所有的集合和他们的数字的并集。
但是怎么保证数字数量等于集合数量呢?
假设左边断了 xxx 条边,剩的 n−xn-xn−x 个集合必然连向不少于 n−xn-xn−x 个数,所以右边断边不少于 n−xn-xn−x 条,总断边数不少于 nnn 条。
同时,只断 nnn 条的方案显然存在(只砍一边就行),又由于每条边的权值加上了 INFINFINF,所以显然最终的答案就会断 nnn 条。
回到刚才断边的定义,就是:未选集合+选取元素=n,也就是选取集合数等于选取元素数。
得证。
Description\text{Description}Description
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=3e5+100;
#define ll long long
#define ui unsigned int
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while (isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();} return x*f;
}int n,m;#define inf 1000000000ll
struct node{int to,nxt,cap;
}p[N];
int fi[N],cur[N],cnt;
inline void addline(int x,int y,int w){p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;p[++cnt]=(node){x,fi[y],0};fi[y]=cnt;return;
}
int s,t;
int bel[N],q[N],st,ed;
bool bfs(){q[st=ed=1]=s;memset(bel,0,sizeof(bel));bel[s]=1;while(st<=ed){int now=q[st++];for(int i=cur[now]=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(!p[i].cap||bel[to]) continue;bel[to]=bel[now]+1;q[++ed]=to;}}return bel[t]?1:0;
}
int dfs(int x,int lim){if(!lim||x==t) return lim;int res(0);for(int &i=cur[x];~i;i=p[i].nxt){int to=p[i].to;if(!p[i].cap||bel[to]!=bel[x]+1) continue;int add=dfs(to,min(lim,p[i].cap));res+=add;lim-=add;p[i].cap-=add;p[i^1].cap+=add;if(!lim) break;}if(!res) bel[x]=-1;return res;
}
ll dinic(){ll flow(0),tmp(0);while(bfs()){while((tmp=dfs(s,2e9))) flow+=tmp;}return flow;
}signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();ll sum(0);s=2*n+1;t=s+1;for(int i=1;i<=n;i++) addline(i+n,t,inf);for(int i=1;i<=n;i++){int k=read();for(int j=1;j<=k;j++){int x=read();addline(i,x+n,inf);}}for(int i=1;i<=n;i++){int w=read();sum-=w;addline(s,i,inf-w);}printf("%lld\n",(dinic()-1ll*n*inf)-sum);return 0;
}
/*
*/