B-A Funny Bipartite Graph
題意:給出每一邊的點數都不超過的二分圖,對左邊的點標號從到,對右邊的點標號從到,數據保證不會出現到的連邊,保證左邊的每個點的度數都在到之間。並且給出一個矩陣,在這個矩陣中,如果,那麼左邊的點和右邊的點不可以同時出現,並且要求右邊所有的點都有連邊。求所有方案中的最小值。
做法:特判掉右邊存在某個點無邊可連的情形,直接輸出。否則由於爲正,對於右邊的點沒有必要連超過條邊,因此搜索的上界不超過,且有不少的剪枝空間,可以通過。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
char str[20];
int g[20][20],A[20][20];
int w[20],tag[20],deg[20];
vector<int> go[20],con[20];
int ans=2e9;
int cal(int x,int y) {
if(!y) return 0;
int ans=1;
for(int i=1;i<=y;++i)
ans=(ans*x);
return ans;
}
int n;
void dfs(int cur,int A) {
if(cur==n+1) { ans=min(ans,A);return; }
if(A>=ans) return;
for(auto &v:go[cur]) {
if(!tag[v]) {
for(auto &x:con[v]) tag[x]++;
deg[v]++;
dfs(cur+1,A+cal(w[v],deg[v])-cal(w[v],deg[v]-1));
deg[v]--;
for(auto &x:con[v]) tag[x]--;
}
}
}
void init() {
memset(tag,0,sizeof(tag));
memset(deg,0,sizeof(deg));
for(int i=1;i<=n;i++) {
go[i].clear();
con[i].clear();
}
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
init();
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%s",str+1);
for(int j=1;j<=n;j++) {
g[i][j]=str[j]-'0';
if(g[i][j]) go[j].push_back(i);
}
}
for(int i=1;i<=n;i++) {
scanf("%s",str+1);
for(int j=1;j<=n;j++) {
A[i][j]=str[j]-'0';
if(A[i][j]) {
con[i].push_back(j);
con[j].push_back(i);
}
}
}
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
bool ok=1;
for(int i=1;i<=n;i++) {
if(!go[i].size()) {
ok=0;
break;
}
}
if(!ok) puts("-1");
else {
ans=2e9;
dfs(1,0);
if(ans==2e9) puts("-1");
else printf("%d\n",ans);
}
}
return 0;
}
C-And and Pair
題意:給出一個長度不超過的二進制01串,然後求問有多少個點對滿足並且並且.
做法:顯然是一個答案,否則就可以枚舉的二進制的最高位的位置,統計除去最高位的低位的個數以及的個數。
對於的位置,當前位一定爲,當前一定有兩種選擇,所以爲
對於的位置,枚舉填的位置個數,如果填顯然只能填,而填則有兩種選擇,所以爲。
每個最高位對答案的貢獻是。
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int mod=1e9+7;
const int N=1e5+7;
char s[N];
int fpow(ll x,int y) {
ll ans=1;
while(y) {
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
ll ans=0;
scanf("%s",s+1);
int n=strlen(s+1);
int _1=0,_0=0;
for(int i=n;i>=1;i--) {
if(s[i]=='1') {
ans+=1ll*fpow(3,_1)*fpow(2,_0)%mod;
ans%=mod;
}
if(s[i]=='1') _1++;
else _0++;
}
printf("%lld\n",(ans+1)%mod);
}
return 0;
}
E-Bob’s Problem
題意:給出一張,的帶權圖,邊分爲黑邊和白邊,然後要求你選出一個邊集,邊集裏面的白邊的條數不超過,使得這個圖是完全連通的,如果無法連通輸出,否則求出最大的權值和。
做法:按照先加黑邊再從權值從大往小加白邊的順序向圖中加邊。維護一個最大生成樹,黑邊的權值全部加入到答案,白邊最多加條。然後判斷是否已經連通,否則在判斷白邊是否加滿條,若爲加滿則把剩餘白邊從大到小補滿到圖中。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=1e6+5;
struct Edge{
int u,v,w,c;
bool operator <(const Edge &rhs) const {
if(c<rhs.c) return 1;
else if(c==rhs.c&&w>rhs.w) return 1;
else return 0;
}
}e[M];
int fa[M];
int fnd(int x) {
if(fa[x]==x) return x;
else return fa[x]=fnd(fa[x]);
}
bool cmp(int a,int b) {
return a>b;
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++) {
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].c);
}
sort(e+1,e+1+m);
ll ans=0;
int cnt=0;
vector<int> rest;
rest.clear();
for(int i=1;i<=m;i++) {
if(e[i].c==0) {
int u=e[i].u;
int v=e[i].v;
int U=fnd(u);
int V=fnd(v);
if(U==V) ans+=e[i].w;
else fa[U]=V,ans+=e[i].w;
}
else if(e[i].c==1) {
int u=e[i].u;
int v=e[i].v;
int U=fnd(u);
int V=fnd(v);
if(U==V) rest.push_back(e[i].w);
else {
fa[U]=V;
ans+=e[i].w;
cnt++;
if(cnt==k) break;
}
}
}
set<int> s;
s.clear();
for(int i=1;i<=n;i++)
s.insert(fnd(i));
if(s.size()==1) {
sort(rest.begin(),rest.end(),cmp);
for(int i=0;i<min(k-cnt,(int)rest.size());i++) ans+=rest[i];
printf("%lld\n",ans);
}
else {
printf("%d\n",-1);
}
}
return 0;
}
G-Eating Plan
題意:給出一個的序列,序列的值是全排列的階乘模上998857459,然後給出若干給查詢,每次查詢給出一個數,求最小的區間長度,使得某個區間滿足並且。
做法:注意到998857459是2802的倍數,因此序列中實際有效的值只有2802個,通過2802*2802的預處理對所有的區間暴力按照區間和排序,同時維護區間長度後綴最小值,這樣就可以做到二分做到單次的查詢。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=998857459;
struct Query {
int val,id;
}a[3000];
int tot=0;
int fac[3000];
struct Two {
int val,len;
bool operator <(const Two &rhs) const {
return val<rhs.val;
}
}b[2804*2804/2];
int mi[2804*2804/2];
int p=0;
int main() {
fac[0]=1;
for(int i=1;i<=2802;i++)
fac[i]=(1ll*fac[i-1]*i)%mod;
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
int x;
scanf("%d",&x);
if(x<=2802) {
a[++tot].val=fac[x];
a[tot].id=i;
}
}
for(int i=1;i<=tot;i++) {
int sum=0;
for(int j=i;j<=tot;j++) {
sum=(sum+a[j].val)%mod;
int len=a[j].id-a[i].id+1;
b[++p].val=sum;
b[p].len=len;
}
}
sort(b+1,b+1+p);
for(int i=p;i>=1;i--) {
if(i==p) mi[i]=b[i].len;
else mi[i]=min(b[i].len,mi[i+1]);
}
while(m--) {
int x;
scanf("%d",&x);
int id=lower_bound(b+1,b+1+p,Two{x,0})-b;
if(id==p+1) puts("-1");
else printf("%d\n",mi[id]);
}
return 0;
}
K-Tree
題意:給出一個的樹,求問這個樹上有多少個點對滿足:
兩個點互相不會成爲彼此的祖先。
兩個點的權值,是和的最近公共祖先。
兩個點的距離不會超過。
做法:考慮啓發式合併,先對樹重鏈剖分,然後對於每個子樹的答案,把作爲統計答案,先計算輕邊但不保留信息,再計算重邊且保留信息,最後再去計算輕邊統計答案。用平衡樹維護每個權值對於所有點的深度,暴力統計。
#include<bits/stdc++.h>
#include<bits/extc++.h>
typedef long long ll;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
const int N=2e5+7;
int n,k;
ll ans=0;
vector<int> go[N];
int w[N];
int sz[N],d[N],son[N];
tree<pii, null_type, less<pii>, rb_tree_tag, tree_order_statistics_node_update> t[N];
void dfs(int u) {
sz[u]=1;
for(auto &v:go[u]) {
d[v]=d[u]+1;
dfs(v);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])
son[u]=v;
}
}
void merge(int u,int lca) {
int x=2*w[lca]-w[u];
if(x>=0) ans+=t[x].order_of_key(pii(2*d[lca]-d[u]+k,2e9));
for(auto &v:go[u]) merge(v,lca);
}
void update(int u,int lca,int opt) {
if(opt==1) t[w[u]].insert(pii(d[u],u));
else if(opt==-1) t[w[u]].erase(pii(d[u],u));
for(auto &v:go[u]) update(v,lca,opt);
}
void dsu(int u,bool ok) {
for(auto &v:go[u]) {
if(v==son[u]) continue;
dsu(v,0);
}
if(son[u]) dsu(son[u],1);
for(auto &v:go[u]) {
if(v==son[u]) continue;
merge(v,u);
update(v,u,1);
}
t[w[u]].insert(pii(d[u],u));
if(!ok) update(u,u,-1);
}
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=2;i<=n;i++) {
int fa;
scanf("%d",&fa);
go[fa].push_back(i);
}
dfs(1);
dsu(1,1);
printf("%lld\n",2*ans);
return 0;
}
L-Who is the Champion
題意:簽到題,給出足球賽事的規則。
做法:按照題意模擬。
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int mp[N][N];
struct Player {
int grade,score,id;
bool operator <(const Player &rhs) const {
if(grade>rhs.grade) return 1;
else if(grade==rhs.grade&&score>rhs.score) return 1;
else return 0;
}
}a[N];
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
scanf("%d",&mp[i][j]);
}
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(j==i) continue;
if(mp[i][j]>mp[j][i]) a[i].grade+=3;
else if(mp[i][j]==mp[j][i]) a[i].grade+=1;
a[i].score+=mp[i][j]-mp[j][i];
}
a[i].id=i;
}
sort(a+1,a+1+n);
if(n==1) printf("%d\n",a[1].id);
else {
if(a[1].grade==a[2].grade&&a[1].score==a[2].score)
puts("play-offs");
else printf("%d\n",a[1].id);
}
return 0;
}