A
签到。
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<15)+1;
int t,n;
bool vis[N][15],lose[N];
int num[N][15];
int main(){
scanf("%d",&t);
for(int tt=1; tt<=t; tt++){
scanf("%d",&n);
for(int i=1; i<=(1<<n); i++){
for(int j=1; j<=n; j++){
scanf("%d",&num[i][j]);
}
sort(num[i]+1,num[i]+n+1);
}
memset(vis,0,sizeof(vis));
memset(lose,0,sizeof(lose));
for(int i=1; i<=n; i++){
int a=-1,b=-1;
for(int j=1; j<=(1<<n); j++){
if(lose[j]) continue;
if(a==-1) a=j;
else if(b==-1){
b=j;
int maxai,maxbi;
for(int k=n; k>=1; k--){
if(vis[a][k]) continue;
else{
maxai=k;
break;
}
}
for(int k=n; k>=1; k--){
if(vis[b][k]) continue;
else{
maxbi=k;
break;
}
}
for(int k=maxai-1; k>=1; k--){
if(vis[a][k]) continue;
if(num[a][k]>num[b][maxbi])
maxai=k;
}
for(int k=maxbi-1; k>=1; k--){
if(vis[b][k]) continue;
if(num[b][k]>num[a][maxai])
maxbi=k;
}
if(num[a][maxai]>num[b][maxbi])
lose[b]=1;
else lose[a]=1;
vis[a][maxai]=1;
vis[b][maxbi]=1;
a=-1;
b=-1;
}
}
}
int ans;
for(int i=1; i<=(1<<n); i++)
if(lose[i]==0)
ans=i;
printf("Case #%d: %d\n",tt,ans);
}
return 0;
}
C
推推式子组合数一下。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
int t;
ll n,m;
ll qpow(ll x, ll y){
ll ans=1;
while(y){
if(y&1) ans=ans*x%mod;
y>>=1;
x=x*x%mod;
}
return ans;
}
ll getinv(ll x){
return qpow(x,mod-2);
}
int inv[1000005];
void init(){
for(ll i=1; i<=1000000; i++)
inv[i]=getinv(i);
return;
}
int main(){
init();
scanf("%d",&t);
for(int tt=1; tt<=t; tt++){
scanf("%lld%lld",&n,&m);
ll tmp1=1,tmp2=m%mod,ans=0;
for(int i=0; i<min(n,m); i++){
if(i){
tmp1=tmp1*((n-i)%mod)%mod*inv[i]%mod;
tmp2=tmp2*((m-i)%mod)%mod;
}
//cout<<tmp1<<" "<<tmp2<<endl;
ans=(ans+tmp1*tmp2)%mod;
}
printf("Case #%d: %lld\n",tt,ans);
}
return 0;
}
D
签到。
#include <bits/stdc++.h>
using namespace std;
int n,m,k;
int mx[100003];
int main(){
int T;
scanf("%d",&T);
for(int tt=1;tt<=T;tt++){
memset(mx,0,sizeof(mx));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
mx[v]=max(mx[v],w);
}
long long ans=0;
for(int i=1;i<=m;i++)ans+=mx[i];
printf("Case #%d: %lld\n",tt,ans);
}
}
F
树dp,维护最多的0和1,高精度。
#include<bits/stdc++.h>
using namespace std;
#define bign num
const int N=5005;
const int power = 4; //每次运算的位数为10的power次方,在这里定义为了方便程序实现
const int base = 10000; //10的power次方。
//要压位的时候,只需改power 和 base即可,如压万位高精,那么power = 4, base = 10000
const int MAXL = 200; //数组的长度。
char a[MAXL], b[MAXL];
struct num
{
int a[MAXL];
num() { memset(a, 0, sizeof(a)); } //初始化
num(char *s) //将一个字符串初始化为高精度数
{
memset(a, 0, sizeof(a));
int len = strlen(s);
a[0] = (len+power-1) / power; //数的长度
for (int i=0, t=0, w; i < len ;w *= 10, ++i)
{
if (i % power == 0) { w = 1, ++t; }
a[t] += w * (s[i]-'0');
}
//初始化数组,这里自己模拟一下,应该很容易懂的~
}
void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; } //在末尾添加一个数,除法的时候要用到
void re() { reverse(a+1, a+a[0]+1); } //把数反过来,除法的时候要用到
void print() //打印此高精度数
{
printf("%d", a[ a[0] ]);
//先打印最高位,为了压位 或者 该高精度数为0 考虑
for (int i = a[0]-1;i > 0;--i)
printf("%0*d", power, a[i]);
//这里"%0*d", power的意思是,必须输出power位,不够则前面用0补足
printf("\n");
}
} p,q,ans;
bool operator < (const num &p, const num &q) //判断小于关系,除法的时候有用
{
if (p.a[0] < q.a[0]) return true;
if (p.a[0] > q.a[0]) return false;
for (int i = p.a[0];i > 0;--i)
{
if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
}
return false;
}
num operator + (const num &p, const num &q) //加法,不用多说了吧,模拟一遍,很容易懂
{
num c;
c.a[0] = max(p.a[0], q.a[0]);
for (int i = 1;i <= c.a[0];++i)
{
c.a[i] += p.a[i] + q.a[i];
c.a[i+1] += c.a[i] / base;
c.a[i] %= base;
}
if (c.a[ c.a[0]+1 ]) ++c.a[0];
return c;
}
num operator - (const num &p, const num &q) //减法,也不用多说,模拟一遍,很容易懂
{
num c = p;
for (int i = 1;i <= c.a[0];++i)
{
c.a[i] -= q.a[i];
if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }
}
while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];
//我的习惯是如果该数为0,那么他的长度也是0,方便比较大小和在末尾添加数时的判断。
return c;
}
num operator * (const num &p, const num &q)
//乘法,还是模拟一遍。。其实高精度就是模拟人工四则运算!
{
num c;
c.a[0] = p.a[0]+q.a[0]-1;
for (int i = 1;i <= p.a[0];++i)
for (int j = 1;j <= q.a[0];++j)
{
c.a[i+j-1] += p.a[i]*q.a[j];
c.a[i+j] += c.a[i+j-1] / base;
c.a[i+j-1] %= base;
}
if (c.a[ c.a[0]+1 ]) ++c.a[0];
return c;
}
num operator / (const num &p, const num &q) //除法,这里我稍微讲解一下
{
num x, y;
for (int i = p.a[0];i >= 1;--i) //从最高位开始取数
{
y.add(p.a[i]); //把数添到末尾(最低位),这时候是高位在前,低位在后
y.re(); //把数反过来,变为统一的存储方式:低位在前,高位在后
while ( !(y < q) ) //大于等于除数的时候,如果小于的话,其实答案上的该位就是初始的“0”
y = y - q, ++x.a[i]; //看能减几个除数,减几次,答案上该位就加几次。
y.re(); //将数反过来,为下一次添数做准备
}
x.a[0] = p.a[0];
while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];
return x;
}
bign zero=num("0");
bign one=num("1");
bign two=num("2");
int t,n,fa[N],l[N],r[N],c;
char s[20];
bool cap[N][16];
bign dp[N][2];
bign tot[N];
void dfs(int x){
if(l[x]==0) return;
dfs(l[x]);
dfs(r[x]);
tot[x]=tot[l[x]]*tot[r[x]];
for(int i=0; i<16; i++){
if(!cap[x][i]) continue;
bign tmp[2];
for(int wl=0; wl<=1; wl++){
for(int wr=0; wr<=1; wr++){
tmp[0]=tmp[1]=zero;
for(int li=0; li<=1; li++){
for(int ri=0; ri<=1; ri++){
int ans;
if(i&(1<<(li*2+ri)))
ans=1;
else ans=0;
bign tmpl,tmpr;
if(li==wl)
tmpl=dp[l[x]][wl];
else tmpl=tot[l[x]]-dp[l[x]][wl];
if(ri==wr)
tmpr=dp[r[x]][wr];
else tmpr=tot[r[x]]-dp[r[x]][wr];
tmp[ans]=tmp[ans]+tmpl*tmpr;
}
}
//cout<<x<<" "<<i<<" "<<wl<<" "<<wr<<" "<<tmp[0]<<" "<<tmp[1]<<endl;
//dp[x][0]=max(dp[x][0],tmp[0]);
//dp[x][1]=max(dp[x][1],tmp[1]);
if(dp[x][0]<tmp[0])
dp[x][0]=tmp[0];
if(dp[x][1]<tmp[1])
dp[x][1]=tmp[1];
}
}
}
//cout<<x<<": "<<dp[x][0]<<" "<<dp[x][1]<<endl;
return;
}
int main(){
scanf("%d",&t);
for(int tt=1; tt<=t; tt++){
scanf("%d",&n);
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
for(int i=1; i<=n-1; i++){
scanf("%s",s);
for(int j=0; j<16; j++){
cap[i][j]=s[j]-'0';
}
}
for(int i=2; i<=2*n-1; i++){
scanf("%d",&c);
fa[i]=c;
if(l[c]==0)
l[c]=i;
else{
r[c]=i;
if(l[c]>r[c])
swap(l[c],r[c]);
}
}
/*
for(int i=1; i<=n-1; i++){
cout<<i<<":"<<endl;
cout<<"l:"<<l[i]<<endl;
cout<<"r:"<<r[i]<<endl;
}
*/
for(int i=0; i<=n-1; i++){
dp[i][0]=zero;
dp[i][1]=zero;
tot[i]=zero;
}
for(int i=n; i<=2*n-1; i++){
dp[i][0]=dp[i][1]=one;
tot[i]=two;
}
dfs(1);
printf("Case #%d: ",tt);
dp[1][1].print();
//printf("\n");
}
return 0;
}
G
通过观察猜测到一个结论,s和t的最小割一定是一边是一个点的情况。树dp算出每个点到其余所有点的距离和。最小的取n-1次,次小的取n-2次,以此类推。注意到答案会爆ll,用int128。
#include <bits/stdc++.h>
using namespace std;
inline void read(__int128 &x){
char ch;
bool flag=false;
for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
x=flag?-x:x;
}
inline void write(__int128 x){
static char s[100];
if (x<0) { putchar('-'); x=-x;}
if(!x){ putchar('0'); return; }
__int128 len=0; for(;x;x/=10) s[len++]=x % 10+'0';
for(__int128 i=len-1;i>=0;--i) putchar(s[i]);
}
struct edge{
__int128 to,next,v;
}e[200003];
__int128 head[100003];
__int128 cnt;
void init(){
memset(head,-1,sizeof(head));
cnt=0;
}
void add(__int128 u,__int128 v,__int128 w){
e[cnt].to=v;
e[cnt].next=head[u];
e[cnt].v=w;
head[u]=cnt++;
}
__int128 n;
__int128 siz[100003];
__int128 dp[100003];
__int128 ans[100003];
void dfs(__int128 u,__int128 fa){
siz[u]=1;
dp[u]=0;
for(__int128 i=head[u];~i;i=e[i].next){
__int128 v=e[i].to;
if(v==fa)continue;
dfs(v,u);
siz[u]+=siz[v];
dp[u]+=dp[v];
dp[u]+=siz[v]*e[i].v;
}
}
void dfs2(__int128 u,__int128 fa,__int128 val){
if(fa==0)ans[u]=dp[u];
else{
ans[u]=ans[fa];
ans[u]-=val*siz[u];
ans[u]+=val*(n-siz[u]);
}
for(__int128 i=head[u];~i;i=e[i].next){
__int128 v=e[i].to;
if(v==fa)continue;
dfs2(v,u,e[i].v);
}
}
int main(){
__int128 T;
read(T);
for(__int128 tt=1;tt<=T;tt++){
init();
read(n);
__int128 u,v,w;
for(__int128 i=1;i<n;i++){
read(u);
read(v);
read(w);
add(u,v,w);
add(v,u,w);
}
dfs(1,0);
dfs2(1,0,0);
__int128 sum=0;
sort(ans+1,ans+1+n);
for(__int128 i=1;i<=n;i++)sum+=(n-i)*ans[i];
printf("Case #");
write(tt);
printf(": ");
write(sum);
printf("\n");
}
}
I
按左端点排序,线段树,维护左端点所在区间右端点的最大值。如果切割的位置小于当前间右端点的最大值,暴力向下更新。注意到暴力更新的次数是O(n)的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=200003;
int T;
int n,m;
struct seg{
int l,r,id;
}rec[maxn];
bool cmp(seg a,seg b){
return a.l<b.l;
}
int ord[maxn];
int ans[maxn];
int cnt;
ll res;
int mx[maxn<<2];
int clk;
void build(int u,int l,int r){
if(l==r){
mx[u]=rec[l].r;
return;
}
int mid=(l+r)/2;
build(2*u,l,mid);
build(2*u+1,mid+1,r);
mx[u]=max(mx[2*u],mx[2*u+1]);
}
void update(int u,int l,int r,int ql,int qr,int x){
if(ql>r||qr<l)return;
if(ql<=l&&r<=qr&&mx[u]<x)return;
if(l==r){
cnt++;
res=res*rec[l].id%mod;
mx[u]=-1e9-7;
ans[rec[l].id]=clk;
return;
}
int mid=(l+r)/2;
update(2*u,l,mid,ql,qr,x);
update(2*u+1,mid+1,r,ql,qr,x);
mx[u]=max(mx[2*u],mx[2*u+1]);
}
int main(){
scanf("%d",&T);
for(int tt=1;tt<=T;tt++){
memset(ans,0,sizeof(ans));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&rec[i].l,&rec[i].r);
ord[i]=rec[i].l;
rec[i].id=i;
}
sort(rec+1,rec+1+n,cmp);
sort(ord+1,ord+1+n);
build(1,1,n);
res=0;
clk=0;
printf("Case #%d:\n",tt);
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
x^=res;
int pos=upper_bound(ord+1,ord+1+n,x)-ord-1;
res=1;
cnt=0;
clk++;
update(1,1,n,1,pos,x);
if(cnt==0)res=0;
printf("%d\n",cnt);
}
for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
}
}
J
注意到数据是随机生成的,n比较大时,答案很快趋近于unsigned long long的上界。于是可以机智的做一些剪枝。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned int uint;
typedef unsigned long long ll;
int n;
uint x,y,z;
inline uint tang(){
uint t;
x^=x<<16;
x^=x>>5;
x^=x<<1;
t=x;
x=y;
y=z;
z=t^x^y;
return z;
}
uint a[10000003];
uint b[10000003];
int tot;
int main(){
int T;
scanf("%d",&T);
for(int tt=1;tt<=T;tt++){
scanf("%d%u%u%u",&n,&x,&y,&z);
ll ans=0;
ll one=1;
tot=0;
for(int i=1;i<=n;i++){
b[i]=tang();
if(n>=10000)
if(b[i]<(1ll<<31)+(1<<30))continue;
if(n>=100000)
if(b[i]<(1ll<<31)+(1<<30)+(1<<29)+(1<<28))continue;
a[++tot]=b[i];
/*mx[i]=max(mx[i-1],a[i]);
for(int j=i-1;j>=1;j--){
if(one*mx[j]*a[i]<ans)break;
ans=max(ans,one*a[i]*a[j]/__gcd(a[i],a[j]));
}*/
}
//cout<<tot<<endl;
sort(a+1,a+1+tot);
for(int i=tot;i>=1;i--){
if(one*a[i]*a[i]<ans)break;
for(int j=i-1;j>=1;j--){
if(one*a[j]*a[i]<ans)break;
ans=max(ans,one*a[i]*a[j]/__gcd(a[i],a[j]));
}
}
printf("Case #%d: %llu\n",tt,ans);
}
}