文章目录
前言
比较水的远古比赛
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 , A 2,3,4,5,6,7,8,9,10,J,Q,K,A 2,3,4,5,6,7,8,9,10,J,Q,K,A。其中规定 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 2,3,4,5,6,7,8,9,10 2,3,4,5,6,7,8,9,10 的点数为 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 2,3,4,5,6,7,8,9,10 2,3,4,5,6,7,8,9,10, J , Q , K J,Q,K J,Q,K 的点数均为 10 10 10, A A A 的点数同时为 1 1 1 或 11 11 11,这取决于玩家的意愿。虽然扑克牌有花色,但是一张卡牌的点数与其花色无关。这个游戏的规则很简单:拿两张牌,如果这两张牌的点数之和等于 n n n,玩家就赢了,否则玩家就输了。
现在玩家已经拿了一张黑桃 Q Q Q,求在其他牌中再抽一张,能使玩家赢得游戏的方案数。
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_JUDGE
freopen("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
有个人要做 n n n 道选择题,必须按 1 ∼ n 1\sim n 1∼n 的顺序答题,第 i i i 题有 a i a_i ai 个选项。不幸的是,这些题这个人一道也不会,只能猜选项,但是他的记忆非常好,可以记住所有题曾经的正确选项。当他做错一道题时,他就必须从 1 1 1 重新开始选,假设题目的正确选项不会变,在最坏的情况下,若要做对所有题,他一共选了多少次选项?
1 ≤ n ≤ 100 , 1 ≤ a i ≤ 1 0 9 1\leq n\leq 100,1\leq a_i \leq 10^9 1≤n≤100,1≤ai≤109
Solution \text{Solution} Solution
个人心中签到题的榜样。
没码量,又有一点点思维。
对于第
i
i
i 关单独考虑考虑。
首先,每个错误选项都会且只会选一次,也就是
1
×
(
a
i
−
1
)
1\times(a_i-1)
1×(ai−1)。
对于正确选项,它会被选做到这一关的次数,换句话说就是之后答错的次数
+
1
+1
+1,也就是
∑
j
=
i
+
1
n
(
a
j
−
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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=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
给出一个图,判断它是不是一个环的大小不小于 3 3 3 的奇环树。
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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=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
两个傻瓜玩俄罗斯轮盘(轮流开枪,直到一方死亡),
n
n
n 个位置里有
k
k
k 个子弹,请你填装这
k
k
k 个子弹,使先开枪者死亡的概率最小,在满足该条件的情况下最小化方案的字典序(空弹夹字典序更小)。
每次询问位置
x
x
x 是否装有子弹。
k
≤
n
≤
1
0
18
k\le n\le10^{18}
k≤n≤1018
Solution \text{Solution} Solution
有点小细节的一道题。
(样例真心良心)
有子弹的地方先手必然死,所以我们就是让空弹夹先手死的尽可能少就行了。
通过观察样例二可以发现,从后往前,隔一个放一颗子弹是一种很好的方案。
但是观察样例三可以发现,在刚才那种构造的基础上,有的时候可以把一枚最前面的子弹挪到后面挨着放,死亡概率不变,但字典序更小。
具体的,这种情况是在前面空出的连续空白段长度为偶数时成立,这时候前面去掉一个必胜位置的数量不变。
还有一些其它边边角角的情况,在 2 k ≥ n 2k\ge n 2k≥n 时,后面挨着放前面隔着放即可;还要注意 n = 1 n=1 n=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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=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
一个序列 a a a , m m m 次询问,每次询问给出 t , k t, k t,k。求 a t + a t + k + a t + 2 k + ⋯ + a t + p k a_t + a_{t+k}+a_{t+2k}+\cdots+a_{t+pk} at+at+k+at+2k+⋯+at+pk 其中 t + p k ≤ n t+pk \leq n t+pk≤n 且 t + ( p + 1 ) k > n t+(p+1)k > n t+(p+1)k>n。
n , m ≤ 300000 , a i ≤ 1 0 9 n,m \leq 300000,a_i \leq 10^9 n,m≤300000,ai≤109
Solution \text{Solution} Solution
似乎题解有对前缀和进行分块优化空间从而在线的神仙做法?
但是这题离线就挺香的了
不难想到根号分治,对于
k
>
n
k>\sqrt n
k>n 的询问,直接暴力即可。
对于
k
≤
n
k\le \sqrt n
k≤n 的询问,离线下来对于
n
\sqrt n
n 个可能的
k
k
k 分别处理前缀和就可以
O
(
1
)
O(1)
O(1) 回答所有询问。
时间复杂度
O
(
n
n
)
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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=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
有一个大小为 n n n 的全集,每个元素是一个数,有 n n n 个子集。题目保证任意 k k k 个子集的并的大小 ⩾ k \geqslant k ⩾k 。
每个子集有一个可正可负的权值,你需要选出一些子集使得这些子集并的大小等于子集个数,且所选子集的权值和最小。可以为空集。
n
≤
300
n\le 300
n≤300
Solution \text{Solution} Solution
一道网络流比较神的题。
首先可以补集转化,选的权值最小,转化为不选的权值最大。
然后由于这种题大多数都是转成最小割,所以把所有权值取反。
然后就是关键的建图:
原点向所有集合连容量等于集合权值
I
N
F
INF
INF 的边,集合向包含的数字连
I
N
F
INF
INF 的边,数字向汇点连
I
N
F
INF
INF 的边。
显然,断数字和集合之间的边是不优的;而其中集合断边相当于不选,数字断边相当于选。
这样,考虑任意一种割的方案,都说明选择了所有的集合和他们的数字的并集。
但是怎么保证数字数量等于集合数量呢?
假设左边断了
x
x
x 条边,剩的
n
−
x
n-x
n−x 个集合必然连向不少于
n
−
x
n-x
n−x 个数,所以右边断边不少于
n
−
x
n-x
n−x 条,总断边数不少于
n
n
n 条。
同时,只断
n
n
n 条的方案显然存在(只砍一边就行),又由于每条边的权值加上了
I
N
F
INF
INF,所以显然最终的答案就会断
n
n
n 条。
回到刚才断边的定义,就是:未选集合+选取元素=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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
memset(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;
}
/*
*/