感谢wxy_大佬给的E的思路
A
考场:n很小,随机化1e3次就好了。
正解:直接从1-n开始加,假如sum=x就交换 \(a[i] \ and \ a[i+1]\)
无解的情况就是 \(\sum_{i=1}^{n}a[i]=x\)
#include <bits/stdc++.h>
#define N (int)(1e3+5)
using namespace std;
int a[N],n,m,t;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
int main() {
t=rd();
while(t--) {
n=rd(); m=rd();
for(int i=1;i<=n;i++) a[i]=rd();
int fl=0;
for(int i=1;i<=999;i++) {
random_shuffle(a+1,a+1+n);
int res=0,ok=1;
for(int j=1;j<=n;j++) {
res+=a[j];
if(res==m) ok=0;
}
if(ok) {
puts("YES");
for(int j=1;j<=n;j++) printf("%d ",a[j]);
puts(""); fl=1;
break;
}
}
if(!fl) puts("NO");
}
return 0;
}
B
手摸下可以发现一个单位正方形只能由2个/4个正方形组成,考虑除以2 or 4之后是否是完全平方数
#include <bits/stdc++.h>
using namespace std;
int t,n;
int xx(int x) {
return x*x;
}
int main() {
cin>>t;
while(t--) {
cin>>n;
if(n&1) puts("NO");
else {
bool fl=0;
for(int i=2;i<=n;i*=2) {
if(n%i==0&&xx(sqrt(n/i))==n/i) fl=1;
}
if(fl) puts("YES");
else puts("NO");
}
}
return 0;
}
C
考场:考虑2个的时候4个数 a b c d 升序
若这样选 ac bd
假设最优
a+c-b-d<a+d-b-c
得 a+b+2c<a+b+2d 显然成立
所以sort一遍 每m个一组 最后判断是否不超过x
别人的:优先队列找当前最小即可
#include <bits/stdc++.h>
#define N (int)(1e5+5)
using namespace std;
struct node {
int x,id;
}a[N];
int b[N],be[N],n,m,k,t;
bool cmp(node x,node y) {
return x.x<y.x;
}
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
int main() {
t=rd();
while(t--) {
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(be,0,sizeof(be));
n=rd(); m=rd(); k=rd();
for(int i=1;i<=n;i++) a[i].x=rd(),a[i].id=i;
sort(a+1,a+1+n,cmp);
int las=0,tot=0;
for(int i=1;i<=n;i++) {
int id=(i-1)/m+1;
if(las!=id) las=id,tot=0;
b[++tot]+=a[i].x; be[a[i].id]=tot;
}
int mx=0,mi=0x7fffffff;
for(int i=1;i<=m;i++) mx=max(mx,b[i]),mi=min(mi,b[i]);
//cout<<mx<<" "<<mi<<endl;
if(mx-mi>k) {
puts("NO"); continue;
}
puts("YES");
for(int i=1;i<=n;i++) printf("%d ",be[i]);
puts("");
}
}
D
考虑l=r的情况,我们可以发现,ans=[1,l]在[l+1,r]没出现的个数。
考虑类比到l<r,最优策略应该是先统计有出现的,再将[l+1,n]中出现次数大于1的给到[1,l]总共给cnt/2个。
l>r同理
#include <bits/stdc++.h>
#define N (int)(2e5+5)
using namespace std;
bool vis[N];
int c[N],a[N],n,l,r,t;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
int main() {
t=rd();
while(t--) {
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
n=rd(); l=rd(); r=rd();
for(int i=1;i<=n;i++) a[i]=rd();
if(l==r) {
int res=0;
for(int i=1;i<=l;i++) ++c[a[i]];
for(int i=l+1;i<=n;i++) if(!c[a[i]]) ++res; else --c[a[i]];
printf("%d\n",res);
} else if(l<r) {
// sort(a+1,a+l+1); sort(a+l+2,a+n+1);
int le=(r-l)/2,res=0,mx=0;
for(int i=l+1;i<=n;i++) ++c[a[i]],mx=max(mx,a[i]);
//for(int i=l+1;i<=n;i++) vis[a[i]]=1;
for(int i=1;i<=l;i++) if(!c[a[i]]) ++res; else --c[a[i]];
for(int i=1;i<=mx;i++) {
if(le<=0) break;
if(c[i]>1) {
if(c[i]/2>le) res+=le;
else res+=c[i]/2;
le-=c[i]/2;
}
}
if(le>0) res+=le*2;
printf("%d\n",res);
} else {
int le=(l-r)/2,res=0,mx=0;
for(int i=1;i<=l;i++) ++c[a[i]],mx=max(mx,a[i]);
for(int i=l+1;i<=n;i++) if(!c[a[i]]) ++res; else --c[a[i]];
for(int i=1;i<=mx;i++) {
if(le<=0) break;
if(c[i]>1) {
if(c[i]/2>le) res+=le;
else res+=c[i]/2;
le-=c[i]/2;
}
}
if(le>0) res+=le*2;
printf("%d\n",res);
}
}
}
E
考虑令开机的为1不开机的为0,那么一定不存在连续超过2个0,且每段1之间必定只有1个0间隔。
由于n只有400,考虑dp。
设f[i][j]为到i时分j段1的方案数。
初始化即 f[1][1]=1 f[i][1]=f[i-1][1]*2。
即设前面那段为a,后面单独的为b,有2种方案: ab/ba。
先枚举i,之后枚举j表示当前分几段,后面枚举k,表示第j+1段的len。
\[f[i+k+1][j+1]=(f[i+k+1][j+1]+f[i][j]*C[i+k+1-j][k]%mod*p[k-1]%mod)%mod; \]首先 i+k+1代表着当前的长度(原来为i,这一段为k,间隔的0为1)
$ f[i][j]C[i+k+1-j][k]p[k-1]$ 代表 到i分j段的方案数乘上当前所有的1(因为j+1段,所以j个间隔0)选出k个来作为新段的组合方案再乘上k个1任意排列的方案数。
#include <bits/stdc++.h>
#define N 405
#define int long long
using namespace std;
int f[N][N],C[N][N],p[N],mod,n;
signed main() {
cin>>n>>mod;
f[1][1]=p[0]=1;
for(int i=2;i<=n;i++) f[i][1]=(f[i-1][1]*2)%mod;
for(int i=1;i<=n;i++) p[i]=(p[i-1]*2)%mod;
for(int i=0;i<N;i++) {
C[i][0]=1;
for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=i;j++) {
for(int k=1;i+k+1<=n;k++) {
f[i+k+1][j+1]=(f[i+k+1][j+1]+f[i][j]*C[i+k+1-j][k]%mod*p[k-1]%mod)%mod;
}
}
}
int answ=0;
for(int i=0;i<=n;i++) answ=(answ+f[n][i])%mod;
cout<<answ;
return 0;
}
F
首先, \((n-1)*x>sum\) 肯定不行,因为不会凭空产生。
反之,必定可以,因为总会有一种排列使得所有的前缀和都大于x*i。
我们考虑这是在树上的,当前节点为x,子节点为y。
我们可以发现,应该先考虑叶子节点,再从底层转移到上层,所以先dfs(y)。
考虑 \(val[x]+val[y]>=x\) ,于是我们直接顺序记录进去,并更新 \(val[x]=val[x]+val[y]-x\)。
否则,我们应该从x去修到y,倒序记录这条边,因为要修好x及其以上的节点才能推到y。
关于路径记录考虑开个双端队列即可。
#include <bits/stdc++.h>
#define N (int)(3e5+5)
#define int long long
using namespace std;
struct edge{
int nex,to,id;
}e[N<<1];
bool vis[N];
int ans[N],ct1,ct2,head[N],cnt;
int n,m,v,a[N];
void dfs(int x,int fa) {
if(vis[x]) return;
vis[x]=1;
for(int i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(y==fa||vis[y]) continue;
dfs(y,x);
if(a[x]+a[y]>=v) {
a[x]=a[x]+a[y]-v;
ans[ct1++]=e[i].id;
} else {
ans[--ct2]=e[i].id;
}
}
}
void add(int x,int y,int z) {
e[++cnt]=edge{head[x],y,z};
head[x]=cnt;
}
signed main() {
cin>>n>>m>>v;
int sum=0;
for(int i=1;i<=n;i++) {
cin>>a[i]; sum+=a[i];
}
if(sum<(n-1)*v) {
puts("NO"); return 0;
}
int x,y;
for(int i=1;i<=m;i++) {
cin>>x>>y;
add(x,y,i); add(y,x,i);
}
// for(int i=1;i<=n;i++) add(0,i,-1);
//g.push_back(1);
ct2=n-1;
dfs(1,0);
puts("YES");
for(int i=0;i<n-1;i++) cout<<ans[i]<<endl;
return 0;
}
G
#include <bits/stdc++.h>
#define N (int)(2e5+5)
#define int long long
using namespace std;
struct edge {
int st,nex,to,w;
}e[N<<1];
int head[N],vis[N],cnt,low[N],dfn[N],tot,col,id[N],val[N],flag[N],dep[N],gc[N];
int n,m,t;
int rd() {
int f=1,sum=0; char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void add(int x,int y,int z) {
e[++cnt]=edge{x,head[x],y,z};
head[x]=cnt;
}
int gcd(int x,int y) {
return y==0?x:gcd(y,x%y);
}
stack<int>s;
void tarjan(int x) {
dfn[x]=low[x]=++tot;
flag[x]=1; s.push(x);
for(int i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(!dfn[y]) {
dep[y]=dep[x]+e[i].w;
tarjan(y);
low[x]=min(low[x],low[y]);
} else if(flag[y]) {
low[x]=min(low[x],dfn[y]);
gc[x]=gcd(gc[x],dep[x]+e[i].w-dep[y]);
}
}
if(low[x]==dfn[x]) {
int qwq=0;
++col;
do {
qwq=s.top(); s.pop();
id[qwq]=col; flag[qwq]=0;
} while(qwq!=x);
}
}
signed main() {
n=rd(); m=rd();
int x,y,z;
for(int i=1;i<=m;i++) {
x=rd(); y=rd(); z=rd();
add(x,y,z);
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++) val[id[i]]=gcd(val[id[i]],gc[i]);
t=rd();
while(t--) {
x=rd(); y=rd(); z=rd();
//cout<<y<<" "<<val[id[x]]<<" "<<z<<endl;
if(!id[x]) puts("NO");
else if(y%gcd(val[id[x]],z)==0) puts("YES");
else puts("NO");
}
return 0;
}