E
签到题,看起来像是博弈论,其实仔细思考后发现,一个石子堆为偶数一定给两人贡献相同,实际上对答案有贡献的是奇数堆,那么只要统计奇数堆的个数就行了。
#include <bits/stdc++.h>
using namespace std;
int input(){
int x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
int n;
int a,Ans,cnt;
int main(){
n=input();
for(int i=1;i<=n;i++){
a=input();
if(a%2==1) cnt++;
Ans+=a/2;
}
Ans+=cnt%2? cnt/2+1:cnt/2;
printf("%d
",Ans);
}
K
签到题,随便手玩一下不难发现计算答案时除去(a_1)和(a_2)之间的符号无法改变,其他项之间都能找到相反的符号排列方式。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const ll mod=1e9+7;
const int N=4007;
int a[N];
int main(){
int n=input();
for(int i=1;i<=n;i++){
a[i]=input();
}
printf("%d
",(a[1]-a[2]+mod)%mod);
}
H
首先策略肯定是:存在某个阈值k,使得小于等于k时就给对面,否则自己拿着。然后设期望差值为s。然后讨论第一轮的数i是啥,如果i小于等于k,那么最后的ans=s-i,因为第二轮先手不变,期望值不变。如果i大于k,那么最后的ans=i-s,因为先后手交换,期望值取反。所以有(E(Ans)=frac{1}{m}(sum_{i=1}^{k}(s-i)+sum_{i=k+1}^{m}(i-s)))。这个式子肯定把(k)设为(ans)的时候取最大值所以有(E(Ans)=frac{1}{m}(sum_{i=1}^{m}|Ans-i|))。(官方给的题解表达方式是(dp_n=frac{1}{m}(sum_{i=1}^{m}max(dp_{n-1}-i,i-dp_{n-1}))=frac{1}{m}(sum_{i=1}^{m}|dp_{n-1}-i|)))
要解这个东西我们可以先考虑二分答案的整数部分,然后再计算答案。
我们不妨设答案为(x)。那么有:
所以我们可以通过(lfloor x floor)计算出答案。然后要得到答案可以通过二分用(mx=sum_{mid}+sum_{m-mid})来获得(lfloor x floor)。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
const ll mod=1e9+7;
ll powmod(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll sum(ll x){
return x*(1+x)/2;
}
int main(){
int T=input();
while(T--){
ll m=input();
ll l=1,r=m;
while(l<r){
ll mid=(l+r+1)>>1;
if(sum(mid-1)+sum(m-mid)>=mid*m) l=mid;
else r=mid-1;
}
ll x1=(m+l+1)%mod;
ll x2=l*(l+1)%mod*powmod((m-l)%mod,mod-2)%mod;
ll Ans=((x1-x2+mod)%mod*((mod+1)/4))%mod;
printf("%lld
",Ans);
}
}
F
把边按权值排序从大到小加入图中,如果加入树边,且边的端点已经连通,则包含这两端点连通块不合法;如果加入图边,且两点还未连通,那么包含这条边两端点的连通块也不合法。考虑到边权有相同的情况,需要把相同的边一并加入图中。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll input(){
ll x=0,f=0;char ch=getchar();
while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return f? -x:x;
}
#define pb push_back
const int N=1e5+7;
struct bcj{
int fa[N],rank[N],col[N];
int find(int x){
if(fa[x]==x) return x;
int t=find(fa[x]);
if(col[fa[x]]||col[t]) col[x]=1;
fa[x]=t;
return t;
}
void merge(int x,int y){
x=find(x);y=find(y);
if(col[x]<col[y]) swap(x,y);
fa[x]=y;
if(x!=y) rank[y]+=rank[x];
}
}t,g;
struct edge{
int t,u,v,w;
}e[N*2];
int n,m;
bool cmp(edge a,edge b){
if(a.w!=b.w) return a.w>b.w;
if(a.t!=b.t) return a.t>b.t;
return 0;
}
int main(){
n=input(),m=input();
for(int i=1;i<=m;i++){
e[i].t=input(),e[i].u=input(),e[i].v=input(),e[i].w=input();
}
for(int i=1;i<=n;i++){
g.fa[i]=t.fa[i]=i;
g.rank[i]=t.rank[i]=1;
}
sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;){
int j=i;
while(j<=m&&e[i].w==e[j].w){
if(e[j].t==1){
g.merge(e[j].u,e[j].v);
t.merge(e[j].u,e[j].v);
}else g.merge(e[j].u,e[j].v);
j++;
}
for(int k=i;k<j;k++){
int t1=g.find(e[k].u),t2=t.rank[t.find(t1)];
if(t2!=g.rank[t1]) g.col[t1]=1;
}
i=j;
}
vector <int> ans;
for(int i=1;i<=n;i++){
g.find(i);
if(g.col[i]==0&&g.col[g.fa[i]]==0) ans.pb(i);
}
printf("%d
",ans.size());
for(int i=0;i<ans.size();i++){
printf("%d%c",ans[i],i==ans.size()-1? '
':' ');
}
}