Codeforces 2208 & 2204
廃颓 十九の伞に灯がついてる 今日も
在颓废十九岁的伞下灯光亮起 今天也是如此
Codeforces Round 1086 (Div. 2)
答题情况

\(C\) 打的不太好,然后 \(D\) 也没想到
A. Bingo Candies 473

**统计一下每种颜色的数量,如果最大值大于 \(n(n-1)\) (即 \(n^2\) 去掉对角线的 \(n\) 个)那就是 No **
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
cin>>n;
int maxn=0;
for(int i=1;i<=n*n;i++){
a[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
cin>>x;
a[x]++;
maxn=max(maxn,a[x]);
}
}
if(maxn<=n*(n-1)){
cout<<"YES\n";
}
else{
cout<<"NO\n";
}
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
B. Cyclists 996

直接模拟,前 \(k\) 个看成一个小根堆用优先队列实现,后面的看成一个队列,然后着重看胜利条件牌。刚开始如果胜利条件牌在前 \(k\) 个牌里,那就选上它,模拟一下再把它放到队列末尾。初始化优先队列和队列之后循环遍历:一开始先从优先队列里打出最小的牌,放到队列末尾。然后从队列抽牌,如果队列第一个出来是胜利条件牌,就模拟一下再把它放回队列末尾(这时要注意前面队列少了一张牌,所以再从后面队列抽一张)。如果队列第一个出来不是胜利条件牌,就放到前面队列即可。
总体难度不难,小模拟题,多调几下就能出来
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,p,m;
const int N=5005;
int a[N];
inline void solve(){
cin>>n>>k>>p>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
if(n==k){
cout<<(m/a[p])<<'\n';
return;
}
queue<pair<int,int>> behind;
priority_queue<int,vector<int>,greater<int> > q;
int tot=0;
bool flag=false;
int ans=0;
if(p>k){
flag=false;
for(int i=1;i<=k;i++){
q.push(a[i]);
}
for(int i=k+1;i<=n;i++){
if(i==p){
behind.push({a[i],1});
}
else{
behind.push({a[i],0});
}
}
}
else{
flag=true;
tot+=a[p];
ans=1;
for(int i=1;i<=k+1;i++){
if(i==p) continue;
q.push(a[i]);
}
for(int i=k+2;i<=n;i++){
behind.push({a[i],0});
}
behind.push({a[p],1});
}
while(1){
// int tmp=0;
int x=q.top();
q.pop();
// tmp+=x;
tot+=x;
if(tot>m) break;
auto y=behind.front();
behind.pop();
if(y.second==1){
behind.push({x,0});
tot+=y.first;
if(tot>m) break;
ans++;
behind.push({a[p],1});
auto z=behind.front();
behind.pop();
q.push(z.first);
}
else{
behind.push({x,0});
q.push(y.first);
}
// if(tot+tmp>m) break;
}
cout<<ans<<'\n';
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
/*
1
8 4 7 10
3 4 4 2 1 1 4 2
3 4 4 1 1 4 2 2 //2
3 4 4 1 4 2 2 1 //3
3 4 4 4 2 2 1 1 //4
*/
C. Stamina and Tasks 1243


\(n\) 个任务,只能顺序选择选或者不选。选的话会降 \(S\)。
假设从某地方 \(i\) 开始算后面的价值,相当于 \(S\times Value\),这里 \(S\) 其实就是一个系数,不影响后面。
设 \(dp[i]\) 为从 \(i\) 开始算价值(\(S=1\))的最大值,那么从后往前推可以得到转移方程
然后这个 \(dp[i]\) 可以滚动掉
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1e5+5;
int a[N];
int c[N],p[N];
inline void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i]>>p[i];
}
double ans=0.0;
for(int i=n;i>=1;i--){
ans=max(ans,c[i]+(1.0-1.0*p[i]/100)*ans);
}
cout<<fixed<<setprecision(8)<<ans<<'\n';
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
D1. Tree Orientation (Easy Version) 1724



题目大意是给你指定每个节点顺着有向树可以到达的节点,让你给这棵树的边标明方向。
简单版本下 \(n\le 500\) ,所以可以用 \(n^3\) 方法
枚举节点 \(u\) 和节点 \(v\) ,再枚举中间节点 \(k\) ,如果 \(u\) 可以到达 \(v\),并且没有 \(k\) 连通 \(u\) 和 \(v\) ,就说明连边 \(u\rightarrow v\) 是树上的有向边。
找出来所有有向边之后,如果
- 边的数量不是 \(n-1\)
- 检查连通性( \(bfs\),\(dfs\),并查集)之后不连通
- 把边重新连成树后检查一下连通图表和给出的不一样
就输出 \(no\),否则输出 \(yes\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=1005;
vector<pair<int,int>> edges;
inline bool liantong(){
vector<vector<int>> E(n);
for(auto [u,v]:edges){
E[u].push_back(v);
E[v].push_back(u);
}
vector<bool> vis(n,false);
queue<int> q;
q.push(0);//0为根
vis[0]=true;
int cnt=1;
while(!q.empty()){
int now=q.front();
q.pop();
for(int v:E[now]){
if(!vis[v]){
vis[v]=true;
cnt++;
q.push(v);
}
}
}
return cnt==n;
}
bool reach[N][N];
inline void solve(){
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
reach[i][j]=false;
}
}
vector<string> s(n);
bool FFF=false;
for(int i=0;i<n;i++){
cin>>s[i];
if(s[i][i]!='1'){
FFF=true;
}
}
if(FFF){
cout<<"No\n";
return;
}
edges.clear();
for(int u=0;u<n;u++){
for(int v=0;v<n;v++){
if(u==v) continue;
if(s[u][v]=='0') continue;
bool flag=false;
for(int k=0;k<n;k++){
if(k==u||k==v) continue;
if(s[u][k]=='1'&&s[k][v]=='1'){
flag=true;
break;
}
}
if(!flag){
edges.push_back({u,v});
}
}
}
if(edges.size()!=n-1){
cout<<"No\n";
return;
}
if(!liantong()){
cout<<"No\n";
return;
}
vector<vector<int>> E(n);
for(auto [u,v]:edges){
E[u].push_back(v);
}
for(int u=0;u<n;u++){
queue<int> q;
q.push(u);
reach[u][u]=true;
while(!q.empty()){
int now=q.front();
q.pop();
for(int v:E[now]){
if(!reach[u][v]){
reach[u][v]=true;
q.push(v);
}
}
}
}
bool flag=true;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if((reach[i][j]==1&&s[i][j]=='0')||(reach[i][j]==0&&s[i][j]=='1')){
flag=false;
break;
}
}
if(!flag){
cout<<"No\n";
return;
}
}
cout<<"Yes\n";
for(auto [u,v]:edges){
cout<<u+1<<" "<<v+1<<'\n';
}
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
D2. Tree Orientation (Hard Version) 2148
数据范围换成 \(n\le 8000\)
得换成 \(n^2\) 方法了
统计每个点可以到达几个点记作 \(cnt_i\),并降序排列。这样的话 \(cnt_i\) 越大的节点 \(i\),就越是这颗树的核心部分。从大到小排序可以有一个好处,如果一个节点指向另外一堆节点,那么这个根节点的 \(cnt_i\) 肯定比子节点的大,进而排除子节点的干扰。
从 \(1\) 到 \(n\) 遍历 \(u\),然后枚举 \(cnt_i\) 大的节点 \(v\),如果当前遍历节点 \(u\) 可以到达 \(v\),说明边 \(u\rightarrow v\) 是树上一条有向边(反证法:如果不是的话,那肯定中间有节点可以链接 \(u\) 和 \(v\),那这个节点肯定 \(cnt_i\) 更大,之前就遍历过了),再把 \(v\) 的可连通的节点 \(w\) 临时删掉(vis[w]=true)。如此循环到所有边都添加完毕。
之后判断 no 和 yes 的方法和上一题一样。
上一题使用 \(bfs\) 判断连通性,本题用并查集
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=10005;
struct Dsu{
int fa[N];
inline void init(){
for(int i=1;i<=n;i++){
fa[i]=i;
}
}
inline int find(int x){
return (x==fa[x])?x:find(fa[x]);
}
inline void merge(int x,int y){
fa[find(x)]=find(y);
}
}dsu;
bool reach[N];
vector<int> tree[N];
void search(int u){
reach[u]=1;
for(int v:tree[u]){
search(v);
}
}
int id[N],cnt[N];
bool vis[N],e[N][N];
inline void solve(){
cin>>n;
for(int i=1;i<=n;i++){
cnt[i]=0;
tree[i].clear();
}
for(int i=1;i<=n;i++){
id[i]=i;
string s;
cin>>s;
for(int j=1;j<=n;j++){
e[i][j]=s[j-1]-'0';
cnt[i]+=e[i][j];
}
}
sort(id+1,id+n+1,[&](int u,int v)->bool{return cnt[u]>cnt[v];});
vector<pair<int,int>> edges;
for(int u=1;u<=n;u++){
for(int i=1;i<=n;i++){
vis[i]=false;
}
vis[u]=true;
for(int i=1;i<=n;i++){
int v=id[i];
if(!vis[v]&&e[u][v]){
edges.push_back({u,v});
if(edges.size()>=n){
cout<<"No\n";
return;
}
for(int w=1;w<=n;w++){
if(e[v][w]){
vis[w]=true;
}
}
}
}
}
if(edges.size()!=n-1){
cout<<"No\n";
return;
}
sort(edges.begin(),edges.end());
dsu.init();
for(auto [u,v]:edges){
dsu.merge(u,v);
tree[u].push_back(v);
}
int flag=true;
for(int i=1;i<=n;i++){
flag&=(dsu.find(i)==dsu.find(1));
}
if(!flag){
cout<<"No\n";
return;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
reach[j]=0;
}
search(i);
for(int j=1;j<=n;j++){
if(reach[j]!=e[i][j]){
cout<<"No\n";
return;
}
}
}
cout<<"Yes\n";
for(auto [u,v]:edges){
cout<<u<<" "<<v<<'\n';
}
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
E. Counting Cute Arrays 2619
暂无
Educational Codeforces Round 188 (Rated for Div. 2)
A. Passing the Ball 270

纯签到题,发现 \(n\) 范围很小,直接从头开始暴力模拟即可,复杂度 \(O(n)\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
cin>>n;
int maxn=0;
for(int i=1;i<=n*n;i++){
a[i]=0;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
int x;
cin>>x;
a[x]++;
maxn=max(maxn,a[x]);
}
}
if(maxn<=n*(n-1)){
cout<<"YES\n";
}
else{
cout<<"NO\n";
}
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
B. Right Maximum 652

举例 1 3 2 3 1,先选中 '3 1',再选中 '3 2',最后选中 '1'
顺序遍历,记录最大值,如果当前值大于等于记录的最大值,那么答案加一
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int N=105;
int a[114514];
inline void solve(){
cin>>n;
int now=0,ans=0;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(x>=now){
ans++;
now=x;
}
}
cout<<ans<<'\n';
}
//1 3 3 1
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
C. Spring 811

简单的容斥原理,算出 \(A\),\(B\),\(C\) 单独的概率和 \(AB\),\(AC\),\(BC\) 单独的概率,和 \(ABC\) 的概率即可
详见代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c,m;
inline int LCM(int a,int b){
return a*b/__gcd(a,b);
}
inline int S(int x){
return m/x;
}
inline void solve(){
cin>>a>>b>>c>>m;
int A=S(a)-S(LCM(a,b))-S(LCM(a,c))+S(LCM(LCM(a,b),c));
int B=S(b)-S(LCM(a,b))-S(LCM(b,c))+S(LCM(LCM(a,b),c));
int C=S(c)-S(LCM(b,c))-S(LCM(a,c))+S(LCM(LCM(a,b),c));
int AB=S(LCM(a,b))-S(LCM(LCM(a,b),c));
int BC=S(LCM(b,c))-S(LCM(LCM(a,b),c));
int AC=S(LCM(a,c))-S(LCM(LCM(a,b),c));
int ABC=S(LCM(LCM(a,b),c));
int ansA=6*A+3*AB+3*AC+2*ABC;
int ansB=6*B+3*AB+3*BC+2*ABC;
int ansC=6*C+3*BC+3*AC+2*ABC;
cout<<ansA<<" "<<ansB<<" "<<ansC<<'\n';
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
D. Alternating Path 1361

这题题意还是比较绕的,就是说给一个无向图规定方向,如果某节点开始所有路径经过边是正逆正逆循环的就叫完美节点,问最多多少完美节点
这里附上一张二分图示例

只有 \(x\rightarrow y\) 和 \(y \rightarrow x\),没有 \(x \rightarrow x\) 和 \(y \rightarrow y\)

其实就是给图奇偶分层,分成 \(A\) 集合和 \(B\) 集合,然后因为第一条边必须是正向,所以贡献是 \(max(A,B)\)
额外考虑环的情况:
- 奇环的话,就会出现正向正向,所以如果一个图内包含奇环,整张图的贡献清零就行
- 偶环的话,照样可以标记黑白黑白,无影响
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
const int N=2e5+5;
int head[N],nxt[N<<1],to[N<<1],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
return;
}
int dep[N];
int ans=0;
bool vis[N];
bool flag=true;
int A=0,B=0;
inline void dfs(int now,int fa){
if(vis[now]) return;
vis[now]=true;
dep[now]=dep[fa]+1;
if(dep[now]&1){
A++;
}
else{
B++;
}
for(int i=head[now];i;i=nxt[i]){
int y=to[i];
if(dep[y]){
int cnt=dep[now]-dep[y];
if(!(cnt&1)){//ji环
flag=false;
}
// else{
// A+=(cnt+1)/2;
// B+=(cnt+1)/2;
// }
}
else{
dfs(y,now);
}
}
}
inline void solve(){
cin>>n>>m;
tot=0;
for(int i=1;i<=n;i++){
dep[i]=0;
head[i]=0;
vis[i]=false;
}
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
add(u,v),add(v,u);
}
int ans=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
flag=true;
A=0,B=0;
dfs(i,0);
if(flag){
ans+=max(A,B);
// cerr<<ans<<" "<<i<<endl;
}
}
}
cout<<ans<<'\n';
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
E. Sum of Digits (and Again) 1749


也是一道十分巧妙的题。先统计好每个数字出现了几次以及总和,然后循环找第二个数 \(x\)(足够小),然后用规则补全后面,设为 tmp,判断一下出现次数够不够以及所有数总和减去 tmp 总和是不是等于 \(x\)。因为第一个数的总和生成的第二个数 \(x\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
inline int f(int x){
int res=0;
while(x){
res+=x%10;
x/=10;
}
return res;
}
inline void solve(){
string s;
cin>>s;
int n=s.size();
if(n==1){
cout<<s<<'\n';
return;
}
vector<int> cnt(10,0);
int sum=0;
for(int i=0;i<n;i++){
cnt[s[i]-'0']++;
sum+=(s[i]-'0');
}
for(int x=1;x<=9*n;x++){
int ss=x;
string tmp=to_string(ss);
while(ss>9){
ss=f(ss);
tmp+=to_string(ss);
}
vector<int> cnt2(10,0);
int sum2=0;
for(int i=0;i<tmp.size();i++){
cnt2[tmp[i]-'0']++;
sum2+=(tmp[i]-'0');
}
// cerr<<x<<" "<<sum2<<endl;
bool flag=true;
for(int i=0;i<=9;i++){
if(cnt[i]<cnt2[i]){
flag=false;
// cerr<<i<<" "<<flag<<endl;
}
}
// if(flag&&sum==sum2&&cnt[0]==cnt2[0]){
// cout<<tmp<<'\n';
// return;
// }
if(sum-sum2!=x){
flag=false;
}
// cerr<<x<<" "<<sum<<" "<<sum2<<" "<<flag<<endl;
if(flag){
// cerr<<"???????????????????????\n";
for(int i=9;i>=0;i--){
while(cnt[i]-cnt2[i]>0){
cout<<i;
cnt[i]--;
}
}
cout<<tmp<<'\n';
return;
}
}
return;
}
signed main(void){
cin.tie(NULL)->sync_with_stdio(false);
int T=1;
cin>>T;
while(T--){
solve();
}
return 0;
}
F. Sum of Fractions 2222
暂无
G. Grid Path error
暂无


浙公网安备 33010602011771号